libweave: Added Ubuntu example of libweave integration This code is not intended to be used in production. It is insecure and optimal for the sake of simplicity. BUG=brillo:1275 TEST=none Change-Id: I0269160f6bb1b4c523640c2e0e6e2a0e2828dd3f Reviewed-on: https://chromium-review.googlesource.com/296407 Reviewed-by: Vitaly Buka <vitalybuka@chromium.org> Commit-Queue: Vitaly Buka <vitalybuka@chromium.org> Trybot-Ready: Vitaly Buka <vitalybuka@chromium.org> Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/libweave/examples/ubuntu/avahi_client.cc b/libweave/examples/ubuntu/avahi_client.cc new file mode 100644 index 0000000..b14d45c --- /dev/null +++ b/libweave/examples/ubuntu/avahi_client.cc
@@ -0,0 +1,103 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/examples/ubuntu/avahi_client.h" + +#include <cstdlib> +#include <vector> + +#include <avahi-common/error.h> + +namespace weave { +namespace examples { + +namespace { + +void GroupCallback(AvahiEntryGroup* g, + AvahiEntryGroupState state, + AVAHI_GCC_UNUSED void* userdata) { + CHECK_NE(state, AVAHI_ENTRY_GROUP_COLLISION); + CHECK_NE(state, AVAHI_ENTRY_GROUP_FAILURE); +} + +} // namespace + +MdnsImpl::MdnsImpl() { + CHECK_EQ(0, std::system("service avahi-daemon status | grep process || " + "service avahi-daemon start")); + thread_pool_.reset(avahi_threaded_poll_new()); + CHECK(thread_pool_); + + int ret = 0; + client_.reset(avahi_client_new(avahi_threaded_poll_get(thread_pool_.get()), + {}, nullptr, this, &ret)); + CHECK(client_) << avahi_strerror(ret); + + avahi_threaded_poll_start(thread_pool_.get()); + + group_.reset(avahi_entry_group_new(client_.get(), GroupCallback, nullptr)); + + CHECK(group_); +} + +MdnsImpl::~MdnsImpl() { + if (thread_pool_) + avahi_threaded_poll_stop(thread_pool_.get()); +} + +void MdnsImpl::PublishService(const std::string& service_name, + uint16_t port, + const std::map<std::string, std::string>& txt) { + LOG(INFO) << "Publishing service"; + CHECK(group_); + + // Create txt record. + std::unique_ptr<AvahiStringList, decltype(&avahi_string_list_free)> txt_list{ + nullptr, &avahi_string_list_free}; + if (!txt.empty()) { + std::vector<std::string> txt_vector; + for (const auto& i : txt) + txt_vector.push_back(i.first + "=" + i.second); + std::vector<const char*> txt_vector_ptr; + for (const auto& i : txt_vector) + txt_vector_ptr.push_back(i.c_str()); + txt_list.reset(avahi_string_list_new_from_array(txt_vector_ptr.data(), + txt_vector_ptr.size())); + CHECK(txt_list); + } + + int ret = 0; + if (prev_port_ == port && prev_type_ == service_name) { + ret = avahi_entry_group_update_service_txt_strlst( + group_.get(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, {}, GetId().c_str(), + ("_" + service_name + "._tcp").c_str(), nullptr, txt_list.get()); + CHECK_GE(ret, 0) << avahi_strerror(ret); + } else { + prev_port_ = port; + prev_type_ = service_name; + + avahi_entry_group_reset(group_.get()); + CHECK(avahi_entry_group_is_empty(group_.get())); + + ret = avahi_entry_group_add_service_strlst( + group_.get(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, {}, GetId().c_str(), + ("_" + service_name + "._tcp").c_str(), nullptr, nullptr, port, + txt_list.get()); + CHECK_GE(ret, 0) << avahi_strerror(ret); + ret = avahi_entry_group_commit(group_.get()); + CHECK_GE(ret, 0) << avahi_strerror(ret); + } +} + +void MdnsImpl::StopPublishing(const std::string& service_name) { + CHECK(group_); + avahi_entry_group_reset(group_.get()); +} + +std::string MdnsImpl::GetId() const { + return "Weave example " + std::to_string(gethostid()); +} + +} // namespace examples +} // namespace weave
diff --git a/libweave/examples/ubuntu/avahi_client.h b/libweave/examples/ubuntu/avahi_client.h new file mode 100644 index 0000000..6b5d1b0 --- /dev/null +++ b/libweave/examples/ubuntu/avahi_client.h
@@ -0,0 +1,48 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_EXAMPLES_UBUNTU_AVAHI_CLIENT_H_ +#define LIBWEAVE_EXAMPLES_UBUNTU_AVAHI_CLIENT_H_ + +#include <map> +#include <string> + +#include <avahi-client/client.h> +#include <avahi-client/publish.h> +#include <avahi-common/thread-watch.h> + +#include <weave/mdns.h> + +namespace weave { +namespace examples { + +// Example of weave::Mdns implemented with avahi. +class MdnsImpl : public Mdns { + public: + MdnsImpl(); + + ~MdnsImpl() override; + void PublishService(const std::string& service_name, + uint16_t port, + const std::map<std::string, std::string>& txt) override; + void StopPublishing(const std::string& service_name) override; + std::string GetId() const override; + + uint16_t prev_port_{0}; + std::string prev_type_; + + std::unique_ptr<AvahiThreadedPoll, decltype(&avahi_threaded_poll_free)> + thread_pool_{nullptr, &avahi_threaded_poll_free}; + + std::unique_ptr<AvahiClient, decltype(&avahi_client_free)> client_{ + nullptr, &avahi_client_free}; + + std::unique_ptr<AvahiEntryGroup, decltype(&avahi_entry_group_free)> group_{ + nullptr, &avahi_entry_group_free}; +}; + +} // namespace examples +} // namespace weave + +#endif // LIBWEAVE_EXAMPLES_UBUNTU_AVAHI_CLIENT_H_
diff --git a/libweave/examples/ubuntu/curl_http_client.cc b/libweave/examples/ubuntu/curl_http_client.cc new file mode 100644 index 0000000..3ab3497 --- /dev/null +++ b/libweave/examples/ubuntu/curl_http_client.cc
@@ -0,0 +1,146 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/examples/ubuntu/curl_http_client.h" + +#include <base/bind.h> +#include <curl/curl.h> +#include <weave/task_runner.h> + +namespace weave { +namespace examples { + +namespace { + +struct ResponseImpl : public HttpClient::Response { + int GetStatusCode() const { return status; } + std::string GetContentType() const { return content_type; } + const std::string& GetData() const { return data; } + + int status; + std::string content_type; + std::string data; +}; + +size_t WriteFunction(void* contents, size_t size, size_t nmemb, void* userp) { + static_cast<std::string*>(userp) + ->append(static_cast<const char*>(contents), size * nmemb); + return size * nmemb; +} + +} // namespace + +CurlHttpClient::CurlHttpClient(TaskRunner* task_runner) + : task_runner_{task_runner} {} + +std::unique_ptr<HttpClient::Response> CurlHttpClient::SendRequestAndBlock( + const std::string& method, + const std::string& url, + const Headers& headers, + const std::string& data, + ErrorPtr* error) { + std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl{curl_easy_init(), + &curl_easy_cleanup}; + CHECK(curl); + + if (method == "GET") { + CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1L)); + } else if (method == "POST") { + CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPPOST, 1L)); + } else { + CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, + method.c_str())); + } + + CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str())); + + curl_slist* chunk = nullptr; + for (const auto& h : headers) + chunk = curl_slist_append(chunk, (h.first + ": " + h.second).c_str()); + + CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, chunk)); + + if (!data.empty() || method == "POST") { + CHECK_EQ(CURLE_OK, + curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, data.c_str())); + } + + std::unique_ptr<ResponseImpl> response{new ResponseImpl}; + CHECK_EQ(CURLE_OK, + curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, &WriteFunction)); + CHECK_EQ(CURLE_OK, + curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response->data)); + CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HEADERFUNCTION, + &WriteFunction)); + CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HEADERDATA, + &response->content_type)); + + CURLcode res = curl_easy_perform(curl.get()); + if (chunk) + curl_slist_free_all(chunk); + + if (res != CURLE_OK) { + Error::AddTo(error, FROM_HERE, "curl", "curl_easy_perform_error", + curl_easy_strerror(res)); + return nullptr; + } + + const std::string kContentType = "\r\nContent-Type:"; + auto pos = response->content_type.find(kContentType); + if (pos == std::string::npos) { + Error::AddTo(error, FROM_HERE, "curl", "no_content_header", + "Content-Type header is missing"); + return nullptr; + } + pos += kContentType.size(); + auto pos_end = response->content_type.find("\r\n", pos); + if (pos_end == std::string::npos) { + pos_end = response->content_type.size(); + } + + response->content_type = response->content_type.substr(pos, pos_end); + + CHECK_EQ(CURLE_OK, curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, + &response->status)); + return std::move(response); +} + +int CurlHttpClient::SendRequest(const std::string& method, + const std::string& url, + const Headers& headers, + const std::string& data, + const SuccessCallback& success_callback, + const ErrorCallback& error_callback) { + ++request_id_; + ErrorPtr error; + auto response = SendRequestAndBlock(method, url, headers, data, &error); + if (response) { + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&CurlHttpClient::RunSuccessCallback, + weak_ptr_factory_.GetWeakPtr(), success_callback, + request_id_, base::Passed(&response)), + {}); + return request_id_; + } + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&CurlHttpClient::RunErrorCallback, + weak_ptr_factory_.GetWeakPtr(), error_callback, + request_id_, base::Passed(&error)), + {}); +} + +void CurlHttpClient::RunSuccessCallback(const SuccessCallback& success_callback, + int id, + std::unique_ptr<Response> response) { + success_callback.Run(id, *response); +} + +void CurlHttpClient::RunErrorCallback(const ErrorCallback& error_callback, + int id, + ErrorPtr error) { + error_callback.Run(id, error.get()); +} + +} // namespace examples +} // namespace weave
diff --git a/libweave/examples/ubuntu/curl_http_client.h b/libweave/examples/ubuntu/curl_http_client.h new file mode 100644 index 0000000..45e13dc --- /dev/null +++ b/libweave/examples/ubuntu/curl_http_client.h
@@ -0,0 +1,54 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_EXAMPLES_UBUNTU_CURL_HTTP_CLIENT_H_ +#define LIBWEAVE_EXAMPLES_UBUNTU_CURL_HTTP_CLIENT_H_ + +#include <string> + +#include <base/memory/weak_ptr.h> +#include <weave/http_client.h> + +namespace weave { + +class TaskRunner; + +namespace examples { + +// Basic implementation of weave::HttpClient using libcurl. Should be used in +// production code as it's blocking and does not validate server certificates. +class CurlHttpClient : public HttpClient { + public: + explicit CurlHttpClient(TaskRunner* task_runner); + + std::unique_ptr<Response> SendRequestAndBlock(const std::string& method, + const std::string& url, + const Headers& headers, + const std::string& data, + ErrorPtr* error) override; + int SendRequest(const std::string& method, + const std::string& url, + const Headers& headers, + const std::string& data, + const SuccessCallback& success_callback, + const ErrorCallback& error_callback) override; + + private: + void RunSuccessCallback(const SuccessCallback& success_callback, + int id, + std::unique_ptr<Response> response); + void RunErrorCallback(const ErrorCallback& error_callback, + int id, + ErrorPtr error); + + TaskRunner* task_runner_{nullptr}; + int request_id_ = 0; + + base::WeakPtrFactory<CurlHttpClient> weak_ptr_factory_{this}; +}; + +} // namespace examples +} // namespace weave + +#endif // LIBWEAVE_EXAMPLES_UBUNTU_CURL_HTTP_CLIENT_H_
diff --git a/libweave/examples/ubuntu/event_http_server.cc b/libweave/examples/ubuntu/event_http_server.cc new file mode 100644 index 0000000..a8e2139 --- /dev/null +++ b/libweave/examples/ubuntu/event_http_server.cc
@@ -0,0 +1,185 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/examples/ubuntu/event_http_server.h" + +#include <vector> + +#include <base/bind.h> +#include <base/time/time.h> +#include <event2/bufferevent_ssl.h> +#include <openssl/err.h> + +namespace weave { +namespace examples { + +namespace { + +std::string GetSslError() { + char error[1000] = {}; + ERR_error_string_n(ERR_get_error(), error, sizeof(error)); + return error; +} + +bufferevent* BuffetEventCallback(event_base* base, void* arg) { + SSL_CTX* ctx = static_cast<SSL_CTX*>(arg); + return bufferevent_openssl_socket_new( + base, -1, SSL_new(ctx), BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE); +} + +} // namespace + +class HttpServerImpl::RequestImpl : public Request { + public: + explicit RequestImpl(evhttp_request* req) : path_{evhttp_request_uri(req)} { + path_ = path_.substr(0, path_.find("?")); + path_ = path_.substr(0, path_.find("#")); + req_.reset(req); + + data_.resize(evbuffer_get_length(req_->input_buffer)); + evbuffer_remove(req_->input_buffer, data_.data(), data_.size()); + } + + ~RequestImpl() {} + + const std::string& GetPath() const override { return path_; } + std::string GetFirstHeader(const std::string& name) const override { + const char* header = evhttp_find_header(req_->input_headers, name.c_str()); + if (!header) + return {}; + return header; + } + const std::vector<uint8_t>& GetData() const override { return data_; } + + evhttp_request* ReleaseHandler() { return req_.release(); } + + private: + std::vector<uint8_t> data_; + std::unique_ptr<evhttp_request, decltype(&evhttp_cancel_request)> req_{ + nullptr, &evhttp_cancel_request}; + std::string path_; +}; + +HttpServerImpl::HttpServerImpl(event_base* base) : base_{base} { + SSL_load_error_strings(); + SSL_library_init(); + + ctx_.reset(SSL_CTX_new(TLSv1_2_server_method())); + SSL_CTX_set_options(ctx_.get(), SSL_OP_SINGLE_DH_USE | + SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_SSLv2); + + ec_key_.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + CHECK(ec_key_) << GetSslError(); + CHECK_EQ(1, SSL_CTX_set_tmp_ecdh(ctx_.get(), ec_key_.get())) << GetSslError(); + + GenerateX509(); + CHECK_EQ(1, SSL_CTX_use_PrivateKey(ctx_.get(), pkey_.get())) << GetSslError(); + CHECK_EQ(1, SSL_CTX_use_certificate(ctx_.get(), x509_.get())) + << GetSslError(); + + CHECK_EQ(1, SSL_CTX_check_private_key(ctx_.get())) << GetSslError(); + + httpd_.reset(evhttp_new(base_)); + CHECK(httpd_); + httpsd_.reset(evhttp_new(base_)); + CHECK(httpsd_); + + evhttp_set_bevcb(httpsd_.get(), BuffetEventCallback, ctx_.get()); + evhttp_set_gencb(httpd_.get(), ProcessRequestCallback, this); + evhttp_set_gencb(httpsd_.get(), ProcessRequestCallback, this); + + CHECK_EQ(0, evhttp_bind_socket(httpd_.get(), "0.0.0.0", GetHttpPort())); + CHECK_EQ(0, evhttp_bind_socket(httpsd_.get(), "0.0.0.0", GetHttpsPort())); +} + +void HttpServerImpl::GenerateX509() { + x509_.reset(X509_new()); + CHECK(x509_) << GetSslError(); + + X509_set_version(x509_.get(), 2); + + X509_gmtime_adj(X509_get_notBefore(x509_.get()), 0); + X509_gmtime_adj(X509_get_notAfter(x509_.get()), + base::TimeDelta::FromDays(365).InSeconds()); + + pkey_.reset(EVP_PKEY_new()); + CHECK(pkey_) << GetSslError(); + std::unique_ptr<BIGNUM, decltype(&BN_free)> big_num(BN_new(), &BN_free); + CHECK(BN_set_word(big_num.get(), 65537)) << GetSslError(); + auto rsa = RSA_new(); + RSA_generate_key_ex(rsa, 2048, big_num.get(), nullptr); + CHECK(EVP_PKEY_assign_RSA(pkey_.get(), rsa)) << GetSslError(); + + X509_set_pubkey(x509_.get(), pkey_.get()); + + CHECK(X509_sign(x509_.get(), pkey_.get(), EVP_sha256())) << GetSslError(); + + cert_fingerprint_.resize(EVP_MD_size(EVP_sha256())); + uint32_t len = 0; + CHECK(X509_digest(x509_.get(), EVP_sha256(), cert_fingerprint_.data(), &len)); + CHECK_EQ(len, cert_fingerprint_.size()); +} + +void HttpServerImpl::ProcessRequestCallback(evhttp_request* req, void* arg) { + static_cast<HttpServerImpl*>(arg)->ProcessRequest(req); +} + +void HttpServerImpl::NotFound(evhttp_request* req) { + std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(), + &evbuffer_free}; + evbuffer_add_printf(buf.get(), "404 Not Found: %s\n", + evhttp_request_uri(req)); + evhttp_send_reply(req, 404, "Not Found", buf.get()); +} + +void HttpServerImpl::ProcessRequest(evhttp_request* req) { + std::string path = evhttp_request_uri(req); + for (auto i = handlers_.rbegin(); i != handlers_.rend(); ++i) { + if (path.compare(0, i->first.size(), i->first) == 0) { + auto request = std::make_shared<RequestImpl>(req); + i->second.Run(*request, + base::Bind(&HttpServerImpl::ProcessReply, + weak_ptr_factory_.GetWeakPtr(), request)); + return; + } + } + NotFound(req); +} +void HttpServerImpl::ProcessReply(std::shared_ptr<RequestImpl> request, + int status_code, + const std::string& data, + const std::string& mime_type) { + std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(), + &evbuffer_free}; + evbuffer_add(buf.get(), data.data(), data.size()); + evhttp_request* req = request->ReleaseHandler(); + evhttp_add_header(req->output_headers, "Content-Type", mime_type.c_str()); + evhttp_send_reply(req, status_code, "None", buf.get()); +} + +void HttpServerImpl::AddOnStateChangedCallback( + const OnStateChangedCallback& callback) { + callback.Run(*this); +} + +void HttpServerImpl::AddRequestHandler(const std::string& path_prefix, + const OnRequestCallback& callback) { + handlers_.emplace(path_prefix, callback); +} + +uint16_t HttpServerImpl::GetHttpPort() const { + return 7780; +} + +uint16_t HttpServerImpl::GetHttpsPort() const { + return 7781; +} + +const std::vector<uint8_t>& HttpServerImpl::GetHttpsCertificateFingerprint() + const { + return cert_fingerprint_; +} + +} // namespace examples +} // namespace weave
diff --git a/libweave/examples/ubuntu/event_http_server.h b/libweave/examples/ubuntu/event_http_server.h new file mode 100644 index 0000000..32d567e --- /dev/null +++ b/libweave/examples/ubuntu/event_http_server.h
@@ -0,0 +1,70 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_SERVER_H_ +#define LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_SERVER_H_ + +#include <event2/http.h> +#include <evhttp.h> +#include <openssl/ssl.h> + +#include <map> +#include <string> +#include <vector> + +#include <base/memory/weak_ptr.h> +#include <weave/http_server.h> + +namespace weave { +namespace examples { + +// HTTP/HTTPS server implemented with libevent. +class HttpServerImpl : public HttpServer { + public: + class RequestImpl; + + explicit HttpServerImpl(event_base* base); + + void AddOnStateChangedCallback( + const OnStateChangedCallback& callback) override; + void AddRequestHandler(const std::string& path_prefix, + const OnRequestCallback& callback) override; + uint16_t GetHttpPort() const override; + uint16_t GetHttpsPort() const override; + const std::vector<uint8_t>& GetHttpsCertificateFingerprint() const override; + + private: + void GenerateX509(); + static void ProcessRequestCallback(evhttp_request* req, void* arg); + void ProcessRequest(evhttp_request* req); + void ProcessReply(std::shared_ptr<RequestImpl> request, + int status_code, + const std::string& data, + const std::string& mime_type); + void NotFound(evhttp_request* req); + + std::map<std::string, OnRequestCallback> handlers_; + + std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ec_key_{nullptr, + &EC_KEY_free}; + + std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> pkey_{nullptr, + &EVP_PKEY_free}; + + std::unique_ptr<X509, decltype(&X509_free)> x509_{nullptr, &X509_free}; + + std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx_{nullptr, + &SSL_CTX_free}; + std::vector<uint8_t> cert_fingerprint_; + event_base* base_{nullptr}; + std::unique_ptr<evhttp, decltype(&evhttp_free)> httpd_{nullptr, &evhttp_free}; + std::unique_ptr<evhttp, decltype(&evhttp_free)> httpsd_{nullptr, + &evhttp_free}; + base::WeakPtrFactory<HttpServerImpl> weak_ptr_factory_{this}; +}; + +} // namespace examples +} // namespace weave + +#endif // LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_SERVER_H_
diff --git a/libweave/examples/ubuntu/event_task_runner.cc b/libweave/examples/ubuntu/event_task_runner.cc new file mode 100644 index 0000000..e80d7a5 --- /dev/null +++ b/libweave/examples/ubuntu/event_task_runner.cc
@@ -0,0 +1,70 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/examples/ubuntu/event_task_runner.h" + +#include <signal.h> + +namespace weave { +namespace examples { + +namespace { +event_base* g_event_base = nullptr; +} + +void EventTaskRunner::PostDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) { + base::Time new_time = base::Time::Now() + delay; + if (queue_.empty() || new_time < queue_.top().first.first) { + ReScheduleEvent(delay); + } + queue_.emplace(std::make_pair(new_time, ++counter_), task); +} + +void EventTaskRunner::Run() { + g_event_base = base_.get(); + + struct sigaction sa = {}; + sa.sa_handler = [](int signal) { + event_base_loopexit(g_event_base, nullptr); + }; + sigfillset(&sa.sa_mask); + sigaction(SIGINT, &sa, nullptr); + + event_base_loop(g_event_base, EVLOOP_NO_EXIT_ON_EMPTY); + g_event_base = nullptr; +} + +void EventTaskRunner::ReScheduleEvent(base::TimeDelta delay) { + timespec ts = delay.ToTimeSpec(); + timeval tv = {ts.tv_sec, ts.tv_nsec / 1000}; + event_add(task_event_.get(), &tv); +} + +void EventTaskRunner::EventHandler(int, int16_t, void* runner) { + static_cast<EventTaskRunner*>(runner)->Process(); +} + +void EventTaskRunner::FreeEvent(event* evnt) { + event_del(evnt); + event_free(evnt); +} + +void EventTaskRunner::Process() { + while (!queue_.empty() && queue_.top().first.first <= base::Time::Now()) { + auto cb = queue_.top().second; + queue_.pop(); + cb.Run(); + } + if (!queue_.empty()) { + base::TimeDelta delta = std::max( + base::TimeDelta(), queue_.top().first.first - base::Time::Now()); + ReScheduleEvent(delta); + } +} + +} // namespace examples +} // namespace weave
diff --git a/libweave/examples/ubuntu/event_task_runner.h b/libweave/examples/ubuntu/event_task_runner.h new file mode 100644 index 0000000..726a621 --- /dev/null +++ b/libweave/examples/ubuntu/event_task_runner.h
@@ -0,0 +1,59 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_EXAMPLES_UBUNTU_EVENT_TASK_RUNNER_H_ +#define LIBWEAVE_EXAMPLES_UBUNTU_EVENT_TASK_RUNNER_H_ + +#include <queue> +#include <utility> +#include <vector> + +#include <event2/event.h> + +#include <weave/task_runner.h> + +namespace weave { +namespace examples { + +// Simple task runner implemented with libevent message loop. +class EventTaskRunner : public TaskRunner { + public: + void PostDelayedTask(const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) override; + + event_base* GetEventBase() const { return base_.get(); } + + void Run(); + + private: + void ReScheduleEvent(base::TimeDelta delay); + static void EventHandler(int, int16_t, void* runner); + static void FreeEvent(event* evnt); + void Process(); + + using QueueItem = std::pair<std::pair<base::Time, size_t>, base::Closure>; + + struct Greater { + bool operator()(const QueueItem& a, const QueueItem& b) const { + return a.first > b.first; + } + }; + + size_t counter_{0}; // Keeps order of tasks with the same time. + + std::priority_queue<QueueItem, + std::vector<QueueItem>, + EventTaskRunner::Greater> queue_; + + std::unique_ptr<event_base, decltype(&event_base_free)> base_{ + event_base_new(), &event_base_free}; + std::unique_ptr<event, decltype(&FreeEvent)> task_event_{ + event_new(base_.get(), -1, EV_TIMEOUT, &EventHandler, this), &FreeEvent}; +}; + +} // namespace examples +} // namespace weave + +#endif // LIBWEAVE_EXAMPLES_UBUNTU_EVENT_TASK_RUNNER_H_
diff --git a/libweave/examples/ubuntu/file_config_store.cc b/libweave/examples/ubuntu/file_config_store.cc new file mode 100644 index 0000000..b2bfd0a --- /dev/null +++ b/libweave/examples/ubuntu/file_config_store.cc
@@ -0,0 +1,134 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/examples/ubuntu/file_config_store.h" + +#include <sys/stat.h> +#include <sys/utsname.h> + +#include <fstream> +#include <map> +#include <string> +#include <vector> + +namespace weave { +namespace examples { + +const char kSettingsDir[] = "/var/lib/weave/"; +const char kSettingsPath[] = "/var/lib/weave/weave_settings.json"; + +bool FileConfigStore::LoadDefaults(Settings* settings) { + char host_name[HOST_NAME_MAX] = {}; + gethostname(host_name, HOST_NAME_MAX); + + settings->name = host_name; + settings->description = ""; + + utsname uname_data; + uname(&uname_data); + + settings->firmware_version = uname_data.sysname; + settings->oem_name = "Unknown"; + settings->model_name = "Unknown"; + settings->model_id = "AAAAA"; + settings->pairing_modes = {PairingType::kEmbeddedCode}; + settings->embedded_code = "0000"; + return true; +} + +std::string FileConfigStore::LoadSettings() { + LOG(INFO) << "Loading settings from " << kSettingsPath; + std::ifstream str(kSettingsPath); + return std::string(std::istreambuf_iterator<char>(str), + std::istreambuf_iterator<char>()); +} + +void FileConfigStore::SaveSettings(const std::string& settings) { + CHECK(mkdir(kSettingsDir, S_IRWXU) == 0 || errno == EEXIST); + LOG(INFO) << "Saving settings to " << kSettingsPath; + std::ofstream str(kSettingsPath); + str << settings; +} + +void FileConfigStore::OnSettingsChanged(const Settings& settings) { + LOG(INFO) << "OnSettingsChanged"; +} + +std::string FileConfigStore::LoadBaseCommandDefs() { + return R"({ + "base": { + "updateBaseConfiguration": { + "minimalRole": "manager", + "parameters": { + "localDiscoveryEnabled": "boolean", + "localAnonymousAccessMaxRole": [ "none", "viewer", "user" ], + "localPairingEnabled": "boolean" + }, + "results": {} + }, + "identify": { + "minimalRole": "user", + "parameters": {}, + "results": {} + }, + "updateDeviceInfo": { + "minimalRole": "manager", + "parameters": { + "description": "string", + "name": "string", + "location": "string" + }, + "results": {} + } + } + })"; +} + +std::map<std::string, std::string> FileConfigStore::LoadCommandDefs() { + return {{"example", R"({ + "base": { + "updateBaseConfiguration": {}, + "identify": {}, + "updateDeviceInfo": {} + } + })"}}; +} + +std::string FileConfigStore::LoadBaseStateDefs() { + return R"({ + "base": { + "firmwareVersion": "string", + "localDiscoveryEnabled": "boolean", + "localAnonymousAccessMaxRole": [ "none", "viewer", "user" ], + "localPairingEnabled": "boolean", + "network": { + "properties": { + "name": "string" + } + } + } + })"; +} + +std::string FileConfigStore::LoadBaseStateDefaults() { + return R"({ + "base": { + "firmwareVersion": "unknown", + "localDiscoveryEnabled": false, + "localAnonymousAccessMaxRole": "none", + "localPairingEnabled": false + } + })"; +} + +std::map<std::string, std::string> FileConfigStore::LoadStateDefs() { + return {}; +} + +std::vector<std::string> FileConfigStore::LoadStateDefaults() { + return {}; +} + +} // namespace examples +} // namespace weave
diff --git a/libweave/examples/ubuntu/file_config_store.h b/libweave/examples/ubuntu/file_config_store.h new file mode 100644 index 0000000..f6e9b5f --- /dev/null +++ b/libweave/examples/ubuntu/file_config_store.h
@@ -0,0 +1,34 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_EXAMPLES_UBUNTU_FILE_CONFIG_STORE_H_ +#define LIBWEAVE_EXAMPLES_UBUNTU_FILE_CONFIG_STORE_H_ + +#include <map> +#include <string> +#include <vector> + +#include <weave/config_store.h> + +namespace weave { +namespace examples { + +class FileConfigStore : public ConfigStore { + public: + bool LoadDefaults(Settings* settings) override; + std::string LoadSettings() override; + void SaveSettings(const std::string& settings) override; + void OnSettingsChanged(const Settings& settings) override; + std::string LoadBaseCommandDefs() override; + std::map<std::string, std::string> LoadCommandDefs() override; + std::string LoadBaseStateDefs() override; + std::string LoadBaseStateDefaults() override; + std::map<std::string, std::string> LoadStateDefs() override; + std::vector<std::string> LoadStateDefaults() override; +}; + +} // namespace examples +} // namespace weave + +#endif // LIBWEAVE_EXAMPLES_UBUNTU_FILE_CONFIG_STORE_H_
diff --git a/libweave/examples/ubuntu/main.cc b/libweave/examples/ubuntu/main.cc new file mode 100644 index 0000000..b42ab66 --- /dev/null +++ b/libweave/examples/ubuntu/main.cc
@@ -0,0 +1,35 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <weave/device.h> + +#include "libweave/examples/ubuntu/avahi_client.h" +#include "libweave/examples/ubuntu/curl_http_client.h" +#include "libweave/examples/ubuntu/event_http_server.h" +#include "libweave/examples/ubuntu/event_task_runner.h" +#include "libweave/examples/ubuntu/file_config_store.h" +#include "libweave/examples/ubuntu/network_manager.h" + +int main() { + weave::examples::FileConfigStore config_store; + weave::examples::EventTaskRunner task_runner; + weave::examples::CurlHttpClient http_client{&task_runner}; + weave::examples::NetworkImpl network{&task_runner}; + weave::examples::MdnsImpl mdns; + weave::examples::HttpServerImpl http_server{task_runner.GetEventBase()}; + + auto device = weave::Device::Create(); + weave::Device::Options opts; + opts.xmpp_enabled = true; + opts.disable_privet = false; + opts.disable_security = false; + opts.enable_ping = true; + device->Start(opts, &config_store, &task_runner, &http_client, &network, + &mdns, &http_server); + + task_runner.Run(); + + LOG(INFO) << "exit"; + return 0; +}
diff --git a/libweave/examples/ubuntu/network_manager.cc b/libweave/examples/ubuntu/network_manager.cc new file mode 100644 index 0000000..48c817c --- /dev/null +++ b/libweave/examples/ubuntu/network_manager.cc
@@ -0,0 +1,534 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/examples/ubuntu/network_manager.h" + +#include <arpa/inet.h> +#include <fcntl.h> +#include <linux/wireless.h> +#include <netdb.h> +#include <openssl/ssl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <cstdlib> +#include <fstream> + +#include <base/bind.h> +#include <weave/task_runner.h> + +namespace weave { +namespace examples { + +namespace { + +int ForkCmd(const std::string& path, const std::vector<std::string>& args) { + int pid = fork(); + if (pid != 0) + return pid; + + std::vector<const char*> args_vector; + args_vector.push_back(path.c_str()); + for (auto& i : args) + args_vector.push_back(i.c_str()); + args_vector.push_back(nullptr); + + execvp(path.c_str(), const_cast<char**>(args_vector.data())); + NOTREACHED(); +} + +class SocketStream : public Stream { + public: + explicit SocketStream(TaskRunner* task_runner) : task_runner_{task_runner} {} + + ~SocketStream() { CloseBlocking(nullptr); } + + void RunDelayedTask(const base::Closure& success_callback) { + success_callback.Run(); + } + + bool ReadAsync(void* buffer, + size_t size_to_read, + const base::Callback<void(size_t)>& success_callback, + const base::Callback<void(const Error*)>& error_callback, + ErrorPtr* error) { + if (socket_fd_ < 0) { + Error::AddTo(error, FROM_HERE, "socket", "invalid_socket", + strerror(errno)); + return false; + } + int size_read = recv(socket_fd_, buffer, size_to_read, MSG_DONTWAIT); + if (size_read > 0) { + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&SocketStream::RunDelayedTask, + weak_ptr_factory_.GetWeakPtr(), + base::Bind(success_callback, size_read)), + {}); + return true; + } + if (errno == EAGAIN || errno == EWOULDBLOCK) { + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&SocketStream::ReadAsync), + weak_ptr_factory_.GetWeakPtr(), buffer, size_to_read, + success_callback, error_callback, nullptr), + base::TimeDelta::FromMilliseconds(200)); + return true; + } + + ErrorPtr recv_error; + Error::AddTo(&recv_error, FROM_HERE, "socket", "socket_recv_failed", + strerror(errno)); + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(error_callback, base::Owned(recv_error.release())), {}); + return true; + } + + bool WriteAllAsync(const void* buffer, + size_t size_to_write, + const base::Closure& success_callback, + const base::Callback<void(const Error*)>& error_callback, + ErrorPtr* error) { + if (socket_fd_ < 0) { + Error::AddTo(error, FROM_HERE, "socket", "invalid_socket", + strerror(errno)); + return false; + } + const char* buffer_ptr = static_cast<const char*>(buffer); + do { + int size_sent = send(socket_fd_, buffer_ptr, size_to_write, 0); + if (size_sent <= 0) { + ErrorPtr send_error; + Error::AddTo(&send_error, FROM_HERE, "socket", "socket_send_failed", + strerror(errno)); + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(error_callback, base::Owned(send_error.release())), {}); + // Still true as we return error with callback. + return true; + } + size_to_write -= size_sent; + buffer_ptr += size_sent; + } while (size_to_write > 0); + + task_runner_->PostDelayedTask(FROM_HERE, success_callback, {}); + return true; + } + bool FlushBlocking(ErrorPtr* error) { return true; } + + bool CloseBlocking(ErrorPtr* error) { + weak_ptr_factory_.InvalidateWeakPtrs(); + if (socket_fd_ >= 0) { + close(socket_fd_); + socket_fd_ = -1; + } + } + + void CancelPendingAsyncOperations() { + weak_ptr_factory_.InvalidateWeakPtrs(); + } + + bool Connect(const std::string& host, uint16_t port) { + std::string service = std::to_string(port); + addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM}; + addrinfo* result = nullptr; + if (getaddrinfo(host.c_str(), service.c_str(), &hints, &result)) { + LOG(ERROR) << "Failed to resolve host name: " << host; + return false; + } + std::unique_ptr<addrinfo, decltype(&freeaddrinfo)> result_deleter{ + result, &freeaddrinfo}; + + for (const addrinfo* info = result; info != nullptr; info = info->ai_next) { + socket_fd_ = + socket(info->ai_family, info->ai_socktype, info->ai_protocol); + if (socket_fd_ < 0) + continue; + + int flags = fcntl(socket_fd_, F_GETFL, 0); + if (flags == -1) + flags = 0; + fcntl(socket_fd_, F_SETFL, flags | O_NONBLOCK); + + LOG(INFO) << "Connecting..."; + if (connect(socket_fd_, info->ai_addr, info->ai_addrlen) == 0) + break; // Success. + + if (errno == EINPROGRESS) { + fd_set write_fds; + FD_ZERO(&write_fds); + FD_SET(socket_fd_, &write_fds); + + struct timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + + int select_ret = select(socket_fd_ + 1, NULL, &write_fds, NULL, &tv); + if (select_ret != -1 && select_ret != 0) { + break; + } + } + + LOG(ERROR) << "Failed to connect"; + CloseBlocking(nullptr); + } + + return socket_fd_ >= 0; + } + + int GetFd() const { return socket_fd_; } + + private: + TaskRunner* task_runner_{nullptr}; + int socket_fd_{-1}; + + base::WeakPtrFactory<SocketStream> weak_ptr_factory_{this}; +}; + +class SSLStream : public Stream { + public: + explicit SSLStream(TaskRunner* task_runner) : task_runner_{task_runner} {} + + ~SSLStream() { weak_ptr_factory_.InvalidateWeakPtrs(); } + + void RunDelayedTask(const base::Closure& success_callback) { + success_callback.Run(); + } + + bool ReadAsync(void* buffer, + size_t size_to_read, + const base::Callback<void(size_t)>& success_callback, + const base::Callback<void(const Error*)>& error_callback, + ErrorPtr* error) { + int res = SSL_read(ssl_.get(), buffer, size_to_read); + if (res > 0) { + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(), + base::Bind(success_callback, res)), + {}); + return true; + } + + int err = SSL_get_error(ssl_.get(), res); + + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&SSLStream::ReadAsync), + weak_ptr_factory_.GetWeakPtr(), buffer, size_to_read, + success_callback, error_callback, nullptr), + base::TimeDelta::FromSeconds(1)); + return true; + } + + ErrorPtr weave_error; + Error::AddTo(&weave_error, FROM_HERE, "ssl", "socket_read_failed", + "SSL error"); + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind( + &SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(), + base::Bind(error_callback, base::Owned(weave_error.release()))), + {}); + return true; + } + + bool WriteAllAsync(const void* buffer, + size_t size_to_write, + const base::Closure& success_callback, + const base::Callback<void(const Error*)>& error_callback, + ErrorPtr* error) { + int res = SSL_write(ssl_.get(), buffer, size_to_write); + if (res > 0) { + buffer = static_cast<const char*>(buffer) + res; + size_to_write -= res; + if (size_to_write == 0) { + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&SSLStream::RunDelayedTask, + weak_ptr_factory_.GetWeakPtr(), success_callback), + {}); + return true; + } + + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&SSLStream::WriteAllAsync), + weak_ptr_factory_.GetWeakPtr(), buffer, size_to_write, + success_callback, error_callback, nullptr), + base::TimeDelta::FromSeconds(1)); + + return true; + } + + int err = SSL_get_error(ssl_.get(), res); + + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&SSLStream::WriteAllAsync), + weak_ptr_factory_.GetWeakPtr(), buffer, size_to_write, + success_callback, error_callback, nullptr), + base::TimeDelta::FromSeconds(1)); + return true; + } + + ErrorPtr weave_error; + Error::AddTo(&weave_error, FROM_HERE, "ssl", "socket_write_failed", + "SSL error"); + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind( + &SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(), + base::Bind(error_callback, base::Owned(weave_error.release()))), + {}); + return true; + } + + bool FlushBlocking(ErrorPtr* error) { return true; } + + bool CloseBlocking(ErrorPtr* error) { + weak_ptr_factory_.InvalidateWeakPtrs(); + return true; + } + + void CancelPendingAsyncOperations() { + weak_ptr_factory_.InvalidateWeakPtrs(); + } + + bool Init() { + ctx_.reset(SSL_CTX_new(TLSv1_2_client_method())); + CHECK(ctx_); + ssl_.reset(SSL_new(ctx_.get())); + + char endpoint[] = "talk.google.com:5223"; + stream_bio_ = BIO_new_connect(endpoint); + CHECK(stream_bio_); + BIO_set_nbio(stream_bio_, 1); + + while (BIO_do_connect(stream_bio_) != 1) { + CHECK(BIO_should_retry(stream_bio_)); + sleep(1); + } + + SSL_set_bio(ssl_.get(), stream_bio_, stream_bio_); + SSL_set_connect_state(ssl_.get()); + + for (;;) { + int res = SSL_do_handshake(ssl_.get()); + if (res) { + return true; + } + + res = SSL_get_error(ssl_.get(), res); + + if (res != SSL_ERROR_WANT_READ || res != SSL_ERROR_WANT_WRITE) { + return false; + } + + sleep(1); + } + return false; + } + + private: + TaskRunner* task_runner_{nullptr}; + std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx_{nullptr, SSL_CTX_free}; + std::unique_ptr<SSL, decltype(&SSL_free)> ssl_{nullptr, SSL_free}; + BIO* stream_bio_{nullptr}; + + base::WeakPtrFactory<SSLStream> weak_ptr_factory_{this}; +}; + +} // namespace + +NetworkImpl::NetworkImpl(TaskRunner* task_runner) : task_runner_{task_runner} { + SSL_load_error_strings(); + SSL_library_init(); + + DisableAccessPoint(); +} +NetworkImpl::~NetworkImpl() { + DisableAccessPoint(); +} + +void NetworkImpl::AddOnConnectionChangedCallback( + const OnConnectionChangedCallback& listener) { + callbacks_.push_back(listener); +} + +void NetworkImpl::TryToConnect(const std::string& ssid, + const std::string& passphrase, + int pid, + base::Time until, + const base::Closure& on_success) { + if (pid) { + int status = 0; + if (pid == waitpid(pid, &status, WNOWAIT)) { + int sockf_d = socket(AF_INET, SOCK_DGRAM, 0); + CHECK_GE(sockf_d, 0) << strerror(errno); + + iwreq wreq = {}; + snprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "wlan0"); + std::string essid(' ', IW_ESSID_MAX_SIZE + 1); + wreq.u.essid.pointer = &essid[0]; + wreq.u.essid.length = essid.size(); + CHECK_GE(ioctl(sockf_d, SIOCGIWESSID, &wreq), 0) << strerror(errno); + essid.resize(wreq.u.essid.length); + close(sockf_d); + + if (ssid == essid) { + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&NetworkImpl::NotifyNetworkChanged, + weak_ptr_factory_.GetWeakPtr()), + {}); + return task_runner_->PostDelayedTask(FROM_HERE, on_success, {}); + } + pid = 0; // Try again. + } + } + + if (pid == 0) { + pid = ForkCmd("nmcli", + {"dev", "wifi", "connect", ssid, "password", passphrase}); + } + + if (base::Time::Now() >= until) { + task_runner_->PostDelayedTask(FROM_HERE, + base::Bind(&NetworkImpl::NotifyNetworkChanged, + weak_ptr_factory_.GetWeakPtr()), + {}); + return; + } + + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&NetworkImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(), + ssid, passphrase, pid, until, on_success), + base::TimeDelta::FromSeconds(1)); +} + +bool NetworkImpl::ConnectToService(const std::string& ssid, + const std::string& passphrase, + const base::Closure& on_success, + ErrorPtr* error) { + CHECK(!hostapd_started_); + if (hostapd_started_) { + Error::AddTo(error, FROM_HERE, "wifi", "busy", "Running Access Point."); + return false; + } + + TryToConnect(ssid, passphrase, 0, + base::Time::Now() + base::TimeDelta::FromMinutes(1), on_success); +} + +NetworkState NetworkImpl::GetConnectionState() const { + // Forced soft AP. + return NetworkState::kOffline; + + if (std::system("ping talk.google.com -c 1") == 0) + return NetworkState::kConnected; + + if (std::system("nmcli dev")) + return NetworkState::kFailure; + + if (std::system("nmcli dev | grep connecting") == 0) + return NetworkState::kConnecting; + + return NetworkState::kOffline; +} + +void NetworkImpl::EnableAccessPoint(const std::string& ssid) { + if (hostapd_started_) + return; + + // Release wlan0 interface. + CHECK_EQ(0, std::system("nmcli nm wifi off")); + CHECK_EQ(0, std::system("rfkill unblock wlan")); + sleep(1); + + std::string hostapd_conf = "/tmp/weave_hostapd.conf"; + { + std::ofstream ofs(hostapd_conf); + ofs << "interface=wlan0" << std::endl; + ofs << "channel=1" << std::endl; + ofs << "ssid=" << ssid << std::endl; + } + + CHECK_EQ(0, std::system(("hostapd -B -K " + hostapd_conf).c_str())); + hostapd_started_ = true; + + for (size_t i = 0; i < 10; ++i) { + if (0 == std::system("ifconfig wlan0 192.168.76.1/24")) + break; + sleep(1); + } + + std::string dnsmasq_conf = "/tmp/weave_dnsmasq.conf"; + { + std::ofstream ofs(dnsmasq_conf.c_str()); + ofs << "port=0" << std::endl; + ofs << "bind-interfaces" << std::endl; + ofs << "log-dhcp" << std::endl; + ofs << "dhcp-range=192.168.76.10,192.168.76.100" << std::endl; + ofs << "interface=wlan0" << std::endl; + ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl; + } + + CHECK_EQ(0, std::system(("dnsmasq --conf-file=" + dnsmasq_conf).c_str())); + task_runner_->PostDelayedTask(FROM_HERE, + base::Bind(&NetworkImpl::NotifyNetworkChanged, + weak_ptr_factory_.GetWeakPtr()), + {}); +} + +void NetworkImpl::DisableAccessPoint() { + int res = std::system("pkill -f dnsmasq.*/tmp/weave"); + res = std::system("pkill -f hostapd.*/tmp/weave"); + CHECK_EQ(0, std::system("nmcli nm wifi on")); + hostapd_started_ = false; + + task_runner_->PostDelayedTask(FROM_HERE, + base::Bind(&NetworkImpl::NotifyNetworkChanged, + weak_ptr_factory_.GetWeakPtr()), + {}); +} + +void NetworkImpl::NotifyNetworkChanged() { + bool online = GetConnectionState() == NetworkState::kConnected; + for (const auto& i : callbacks_) + i.Run(online); +} + +std::unique_ptr<Stream> NetworkImpl::OpenSocketBlocking(const std::string& host, + uint16_t port) { + std::unique_ptr<SocketStream> stream{new SocketStream{task_runner_}}; + if (!stream->Connect(host, port)) + return nullptr; + return std::move(stream); +} + +void NetworkImpl::CreateTlsStream( + std::unique_ptr<Stream> stream, + const std::string& host, + const base::Callback<void(std::unique_ptr<Stream>)>& success_callback, + const base::Callback<void(const Error*)>& error_callback) { + // Connect to SSL port instead of upgrading to TLS. + std::unique_ptr<SSLStream> tls_stream{new SSLStream{task_runner_}}; + + if (tls_stream->Init()) { + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(success_callback, base::Passed(&tls_stream)), {}); + } else { + ErrorPtr error; + Error::AddTo(&error, FROM_HERE, "tls", "tls_init_failed", + "Failed to initialize TLS stream."); + } +} + +} // namespace examples +} // namespace weave
diff --git a/libweave/examples/ubuntu/network_manager.h b/libweave/examples/ubuntu/network_manager.h new file mode 100644 index 0000000..c58d220 --- /dev/null +++ b/libweave/examples/ubuntu/network_manager.h
@@ -0,0 +1,63 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_EXAMPLES_UBUNTU_NETWORK_MANAGER_H_ +#define LIBWEAVE_EXAMPLES_UBUNTU_NETWORK_MANAGER_H_ + +#include <string> +#include <vector> + +#include <base/memory/weak_ptr.h> +#include <base/time/time.h> +#include <weave/network.h> + +namespace weave { + +class TaskRunner; + +namespace examples { + +// Basic weave::Network implementation. +// Production version of SSL socket needs secure server certificate check. +class NetworkImpl : public Network { + public: + explicit NetworkImpl(TaskRunner* task_runner); + ~NetworkImpl(); + + void AddOnConnectionChangedCallback( + const OnConnectionChangedCallback& listener) override; + bool ConnectToService(const std::string& ssid, + const std::string& passphrase, + const base::Closure& on_success, + ErrorPtr* error) override; + NetworkState GetConnectionState() const override; + void EnableAccessPoint(const std::string& ssid) override; + void DisableAccessPoint() override; + std::unique_ptr<Stream> OpenSocketBlocking(const std::string& host, + uint16_t port) override; + void CreateTlsStream( + std::unique_ptr<Stream> stream, + const std::string& host, + const base::Callback<void(std::unique_ptr<Stream>)>& success_callback, + const base::Callback<void(const Error*)>& error_callback) override; + + private: + void TryToConnect(const std::string& ssid, + const std::string& passphrase, + int pid, + base::Time until, + const base::Closure& on_success); + void NotifyNetworkChanged(); + + bool hostapd_started_{false}; + TaskRunner* task_runner_{nullptr}; + std::vector<OnConnectionChangedCallback> callbacks_; + + base::WeakPtrFactory<NetworkImpl> weak_ptr_factory_{this}; +}; + +} // namespace examples +} // namespace weave + +#endif // LIBWEAVE_EXAMPLES_UBUNTU_NETWORK_MANAGER_H_