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_