shill: vpn: OpenVPN management interface support.
Implement socket binding and message parsing.
BUG=chromium-os:26994
TEST=unit tests
Change-Id: I4fa9b8982c0b194adaac5353abc5e2eabbfa22de
Reviewed-on: https://gerrit.chromium.org/gerrit/19046
Commit-Ready: Darin Petkov <petkov@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/openvpn_management_server.cc b/openvpn_management_server.cc
index 019d653..71bff83 100644
--- a/openvpn_management_server.cc
+++ b/openvpn_management_server.cc
@@ -4,18 +4,151 @@
#include "shill/openvpn_management_server.h"
+#include <netinet/in.h>
+
+#include <base/bind.h>
#include <base/logging.h>
+#include <base/string_split.h>
+#include <base/string_util.h>
+#include <base/stringprintf.h>
+
+#include "shill/event_dispatcher.h"
+#include "shill/sockets.h"
+
+using base::Bind;
+using base::SplitString;
+using base::StringPrintf;
+using std::string;
+using std::vector;
namespace shill {
OpenVPNManagementServer::OpenVPNManagementServer(OpenVPNDriver *driver)
- : driver_(driver) {}
+ : driver_(driver),
+ weak_ptr_factory_(this),
+ ready_callback_(Bind(&OpenVPNManagementServer::OnReady,
+ weak_ptr_factory_.GetWeakPtr())),
+ input_callback_(Bind(&OpenVPNManagementServer::OnInput,
+ weak_ptr_factory_.GetWeakPtr())),
+ sockets_(NULL),
+ socket_(-1),
+ dispatcher_(NULL),
+ connected_socket_(-1) {}
-OpenVPNManagementServer::~OpenVPNManagementServer() {}
+OpenVPNManagementServer::~OpenVPNManagementServer() {
+ Stop();
+}
-bool OpenVPNManagementServer::Init() {
+bool OpenVPNManagementServer::Start(EventDispatcher *dispatcher,
+ Sockets *sockets) {
VLOG(2) << __func__;
- return false;
+ if (sockets_) {
+ return true;
+ }
+
+ int socket = sockets_->Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (socket < 0) {
+ PLOG(ERROR) << "Unable to create management server socket.";
+ return false;
+ }
+
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ if (sockets->Bind(
+ socket, reinterpret_cast<struct sockaddr *>(&addr), addrlen) < 0 ||
+ sockets->Listen(socket, 1) < 0 ||
+ sockets->GetSockName(
+ socket, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0) {
+ PLOG(ERROR) << "Socket setup failed.";
+ sockets->Close(socket);
+ return false;
+ }
+
+ VLOG(2) << "Listening socket: " << socket;
+ socket_ = socket;
+ ready_handler_.reset(
+ dispatcher->CreateReadyHandler(
+ socket, IOHandler::kModeInput, ready_callback_));
+ dispatcher_ = dispatcher;
+ return true;
+}
+
+void OpenVPNManagementServer::Stop() {
+ VLOG(2) << __func__;
+ if (!sockets_) {
+ return;
+ }
+ input_handler_.reset();
+ if (connected_socket_ >= 0) {
+ sockets_->Close(connected_socket_);
+ connected_socket_ = -1;
+ }
+ dispatcher_ = NULL;
+ ready_handler_.reset();
+ if (socket_ >= 0) {
+ sockets_->Close(socket_);
+ socket_ = -1;
+ }
+ sockets_ = NULL;
+}
+
+void OpenVPNManagementServer::OnReady(int fd) {
+ VLOG(2) << __func__ << "(" << fd << ")";
+ connected_socket_ = sockets_->Accept(fd, NULL, NULL);
+ if (connected_socket_ < 0) {
+ PLOG(ERROR) << "Connected socket accept failed.";
+ return;
+ }
+ ready_handler_.reset();
+ input_handler_.reset(
+ dispatcher_->CreateInputHandler(connected_socket_, input_callback_));
+ SendState("on");
+}
+
+void OpenVPNManagementServer::OnInput(InputData *data) {
+ VLOG(2) << __func__ << "(" << data->len << ")";
+ vector<string> messages;
+ SplitString(
+ string(reinterpret_cast<char *>(data->buf), data->len), '\n', &messages);
+ for (vector<string>::const_iterator it = messages.begin();
+ it != messages.end(); ++it) {
+ ProcessMessage(*it);
+ }
+}
+
+void OpenVPNManagementServer::ProcessMessage(const string &message) {
+ VLOG(2) << __func__ << "(" << message << ")";
+ if (StartsWithASCII(message, ">INFO:", true)) {
+ return;
+ }
+ if (StartsWithASCII(message, ">PASSWORD:Need ", true)) {
+ NOTIMPLEMENTED();
+ return;
+ }
+ if (StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
+ NOTIMPLEMENTED();
+ return;
+ }
+ if (StartsWithASCII(message, ">STATE:", true)) {
+ NOTIMPLEMENTED();
+ return;
+ }
+ LOG(WARNING) << "OpenVPN management message ignored: " << message;
+}
+
+void OpenVPNManagementServer::Send(const string &data) {
+ VLOG(2) << __func__ << "(" << data << ")";
+ ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
+ PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
+ << "Send failed: " << data;
+}
+
+void OpenVPNManagementServer::SendState(const string &state) {
+ VLOG(2) << __func__ << "(" << state << ")";
+ Send(StringPrintf("state %s\n", state.c_str()));
}
} // namespace shill