From 3ad19618f81e22b6125baadaa381fa07d712b651 Mon Sep 17 00:00:00 2001 From: Lukasz Wojciechowski Date: Mon, 30 Jun 2014 09:58:18 +0200 Subject: [PATCH] Prepare client socket class for connecting cynara service Class is based on implementation imported from security-server in previous commit. Change-Id: I77643de6ec7321d3f6af0b395e1b1e4bf06b3774 --- src/client/CMakeLists.txt | 1 + src/client/sockets/Socket.cpp | 231 ++++++++++++++++ src/client/sockets/Socket.h | 79 ++++++ src/client/sockets/client-common.cpp | 292 --------------------- .../exceptions/ServerConnectionErrorException.h | 43 +++ 5 files changed, 354 insertions(+), 292 deletions(-) create mode 100644 src/client/sockets/Socket.cpp create mode 100644 src/client/sockets/Socket.h delete mode 100644 src/client/sockets/client-common.cpp create mode 100644 src/common/exceptions/ServerConnectionErrorException.h diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 89fe001..9b5e695 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -24,6 +24,7 @@ SET(CYNARA_LIB_CYNARA_PATH ${CYNARA_PATH}/client) SET(LIB_CYNARA_SOURCES ${CYNARA_LIB_CYNARA_PATH}/api/client-api.cpp ${CYNARA_LIB_CYNARA_PATH}/logic/Logic.cpp + ${CYNARA_LIB_CYNARA_PATH}/sockets/Socket.cpp ) INCLUDE_DIRECTORIES( diff --git a/src/client/sockets/Socket.cpp b/src/client/sockets/Socket.cpp new file mode 100644 index 0000000..cddd05f --- /dev/null +++ b/src/client/sockets/Socket.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/* + * @file Socket.cpp + * @author Bartlomiej Grzelewski + * @author Lukasz Wojciechowski + * @version 1.0 + * @brief This file contains implementation of UNIX client socket class + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Socket.h" + +namespace Cynara { + +Socket::Socket(const std::string &socketPath, int timeoutMiliseconds) : m_sock(-1), + m_socketPath(socketPath), m_pollTimeout(timeoutMiliseconds) { +} + +Socket::~Socket() { + close(); +} + +void Socket::close(void) noexcept { + if (m_sock > -1) + ::close(m_sock); + m_sock = -1; +} + +bool Socket::waitForSocket(int event) { + int ret; + pollfd desc[1]; + desc[0].fd = m_sock; + desc[0].events = event; + + ret = TEMP_FAILURE_RETRY(poll(desc, 1, m_pollTimeout)); + + if (ret == -1) { + int err = errno; + LOGE("'poll' function error [%d] : <%s>", err, strerror(err)); + close(); + throw UnexpectedErrorException(err, strerror(err)); + } else if (ret == 0) { + LOGD("Poll timeout"); + } + + return (ret == 1); +} + +int Socket::getSocketError(void) { + int err = 0; + socklen_t len = sizeof(err); + int ret = getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &err, &len); + if (ret < 0) { + int err = errno; + close(); + LOGE("'getsockopt' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } + return err; +} + +bool Socket::isConnected(void) { + if (m_sock < 0) + return false; + + if (getSocketError() != 0) { + close(); + return false; + } + + return true; +} + +bool Socket::connect(void) { + sockaddr_un clientAddr; + int flags; + + if (isConnected()) + return true; + + close(); + + m_sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (m_sock < 0) { + int err = errno; + LOGE("'socket' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } + + if ((flags = fcntl(m_sock, F_GETFL, 0)) < 0 || + fcntl(m_sock, F_SETFL, flags | O_NONBLOCK) < 0) + { + int err = errno; + close(); + LOGE("'fcntl' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } + + memset(&clientAddr, 0, sizeof(clientAddr)); + + clientAddr.sun_family = AF_UNIX; + + if (m_socketPath.length() >= sizeof(clientAddr.sun_path)) { + close(); + LOGE("Error: socket path <%s> is too long [%zu]. Max len is [%u]", m_socketPath.c_str(), + m_socketPath.length(), sizeof(clientAddr.sun_path)); + throw InitException(); + } + + strcpy(clientAddr.sun_path, m_socketPath.c_str()); + + LOGD("ClientAddr.sun_path <%s>", clientAddr.sun_path); + + int retval = TEMP_FAILURE_RETRY(::connect(m_sock, (struct sockaddr*)&clientAddr, + SUN_LEN(&clientAddr))); + int err = 0; + if (retval == -1) { + err = errno; + if (err == EINPROGRESS) { + if (!waitForSocket(POLLOUT)) { + return false; + } + err = getSocketError(); + } + if (err == ECONNREFUSED) { + //no one is listening + return false; + } + close(); + LOGE("'connect' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } + + return isConnected(); +} + +bool Socket::sendToServer(BinaryQueue &queue) { + ssize_t done = 0; + bool retry = false; + + RawBuffer buffer(queue.size()); + queue.flattenConsume(buffer.data(), queue.size()); + + do { + if (!connect()) { + LOGE("Error connecting to socket"); + throw ServerConnectionErrorException(); + } + + retry = false; + done = 0; + while ((buffer.size() - done) > 0) { + if (! waitForSocket(POLLOUT)) { + LOGE("Error in poll(POLLOUT)"); + throw ServerConnectionErrorException(); + } + ssize_t t = TEMP_FAILURE_RETRY(write(m_sock, buffer.data() + done, + buffer.size() - done)); + if (t == -1) { + int err = errno; + if (err == EPIPE) { + close(); + LOGN("Connection closed by server. Retrying to connect."); + retry = true; + break; + } + close(); + LOGE("'write' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } + done += t; + } + } while (retry); + + return true; +} + +bool Socket::receiveFromServer(BinaryQueue &queue) +{ + if (!waitForSocket(POLLIN)) { + LOGE("Error in poll(POLLIN)"); + throw ServerConnectionErrorException(); + } + + RawBuffer readBuffer(BUFSIZ); + ssize_t size = TEMP_FAILURE_RETRY(read(m_sock, readBuffer.data(), BUFSIZ)); + + if (size == -1) { + int err = errno; + LOGE("'read' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } + + if (size == 0) { + LOGW("read return 0 / Connection closed by server."); + return false; + } + queue.appendCopy(readBuffer.data(), size); + + return true; +} + +} // namespace Cynara diff --git a/src/client/sockets/Socket.h b/src/client/sockets/Socket.h new file mode 100644 index 0000000..7c6a286 --- /dev/null +++ b/src/client/sockets/Socket.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/* + * @file Socket.h + * @author Bartlomiej Grzelewski + * @author Lukasz Wojciechowski + * @version 1.0 + * @brief This file contains definition of UNIX client socket class + */ + +#ifndef SRC_CLIENT_SOCKETS_SOCKET_H_ +#define SRC_CLIENT_SOCKETS_SOCKET_H_ + +#include + +#include + +namespace Cynara { + +class Socket { +private: + int m_sock; + + std::string m_socketPath; + int m_pollTimeout; + + void close(void) noexcept; + + //returns true if socket is ready + //returns false in case of timeout + //throws in critical situations + bool waitForSocket(int event); + + //returns int errorcode read from socket + //throws in critical situations + int getSocketError(void); + +public: + Socket(const std::string &socketPath, int timeoutMiliseconds = -1); + ~Socket(); + + //returns bool connection state (true - connected, false - not connected)) + //throws in critical situations + bool isConnected(void); + + //returns true if connection succeeded + //returns false if connection was timeout or no one is listening + //throws in critical situations + bool connect(void); + + //returns true if data was successfully send to server + //returns false if connection was lost + //throws ServerConnectionErrorException if cannot connect server (or timeout) + //throws other exceptions in critical situations + bool sendToServer(BinaryQueue &queue); + + //returns true if data was successfully read from server + //returns false if connection was lost + //throws ServerConnectionErrorException if cannot connect server (or timeout) + //throws other exceptions in critical situations + bool receiveFromServer(BinaryQueue &queue); +}; + +} // namespace Cynara + +#endif /* SRC_CLIENT_SOCKETS_SOCKET_H_ */ diff --git a/src/client/sockets/client-common.cpp b/src/client/sockets/client-common.cpp deleted file mode 100644 index 00f099f..0000000 --- a/src/client/sockets/client-common.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd All Rights Reserved - * - * Contact: Bumjin Im - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ -/* - * @file client-common.cpp - * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) - * @version 1.0 - * @brief This file is implementation of client-common functions. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -IMPLEMENT_SAFE_SINGLETON(SecurityServer::Log::LogSystem); - -namespace { - -const int POLL_TIMEOUT = 2000; - -void securityClientEnableLogSystem(void) { - SecurityServer::Singleton::Instance().SetTag("SECURITY_SERVER_CLIENT"); -} - -int waitForSocket(int sock, int event, int timeout) { - int retval; - pollfd desc[1]; - desc[0].fd = sock; - desc[0].events = event; - - while((-1 == (retval = poll(desc, 1, timeout))) && (errno == EINTR)) { - timeout >>= 1; - errno = 0; - } - - if (0 == retval) { - LogDebug("Poll timeout"); - } else if (-1 == retval) { - int err = errno; - LogError("Error in poll: " << strerror(err)); - } - return retval; -} - -class SockRAII { -public: - SockRAII() - : m_sock(-1) - {} - - virtual ~SockRAII() { - if (m_sock > -1) - close(m_sock); - } - - int Connect(char const * const interface) { - sockaddr_un clientAddr; - int flags; - - if (m_sock != -1) // guard - close(m_sock); - - m_sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (m_sock < 0) { - int err = errno; - LogError("Error creating socket: " << strerror(err)); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - if ((flags = fcntl(m_sock, F_GETFL, 0)) < 0 || - fcntl(m_sock, F_SETFL, flags | O_NONBLOCK) < 0) - { - int err = errno; - LogError("Error in fcntl: " << strerror(err)); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - memset(&clientAddr, 0, sizeof(clientAddr)); - - clientAddr.sun_family = AF_UNIX; - - if (strlen(interface) >= sizeof(clientAddr.sun_path)) { - LogError("Error: interface name " << interface << "is too long. Max len is:" << sizeof(clientAddr.sun_path)); - return SECURITY_SERVER_API_ERROR_NO_SUCH_SERVICE; - } - - strcpy(clientAddr.sun_path, interface); - - LogDebug("ClientAddr.sun_path = " << interface); - - int retval = TEMP_FAILURE_RETRY(connect(m_sock, (struct sockaddr*)&clientAddr, SUN_LEN(&clientAddr))); - if ((retval == -1) && (errno == EINPROGRESS)) { - if (0 >= waitForSocket(m_sock, POLLIN, POLL_TIMEOUT)) { - LogError("Error in waitForSocket."); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - int error = 0; - socklen_t len = sizeof(error); - retval = getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &error, &len); - - if (-1 == retval) { - int err = errno; - LogError("Error in getsockopt: " << strerror(err)); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - if (error == EACCES) { - LogError("Access denied"); - return SECURITY_SERVER_API_ERROR_ACCESS_DENIED; - } - - if (error != 0) { - LogError("Error in connect: " << strerror(error)); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - return SECURITY_SERVER_API_SUCCESS; - } - - if (-1 == retval) { - int err = errno; - LogError("Error connecting socket: " << strerror(err)); - if (err == EACCES) - return SECURITY_SERVER_API_ERROR_ACCESS_DENIED; - if (err == ENOTSOCK) - return SECURITY_SERVER_API_ERROR_NO_SUCH_SERVICE; - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - return SECURITY_SERVER_API_SUCCESS; - } - - int Get() { - return m_sock; - } - -private: - int m_sock; -}; - -} // namespace anonymous - -namespace SecurityServer { - - -int sendToServer(char const * const interface, const RawBuffer &send, MessageBuffer &recv) { - int ret; - SockRAII sock; - ssize_t done = 0; - char buffer[2048]; - - if (SECURITY_SERVER_API_SUCCESS != (ret = sock.Connect(interface))) { - LogError("Error in SockRAII"); - return ret; - } - - while ((send.size() - done) > 0) { - if (0 >= waitForSocket(sock.Get(), POLLOUT, POLL_TIMEOUT)) { - LogError("Error in poll(POLLOUT)"); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - ssize_t temp = TEMP_FAILURE_RETRY(write(sock.Get(), &send[done], send.size() - done)); - if (-1 == temp) { - int err = errno; - LogError("Error in write: " << strerror(err)); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - done += temp; - } - - do { - if (0 >= waitForSocket(sock.Get(), POLLIN, POLL_TIMEOUT)) { - LogError("Error in poll(POLLIN)"); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - ssize_t temp = TEMP_FAILURE_RETRY(read(sock.Get(), buffer, 2048)); - if (-1 == temp) { - int err = errno; - LogError("Error in read: " << strerror(err)); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - if (0 == temp) { - LogError("Read return 0/Connection closed by server(?)"); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - RawBuffer raw(buffer, buffer+temp); - recv.Push(raw); - } while(!recv.Ready()); - return SECURITY_SERVER_API_SUCCESS; -} - -int sendToServerAncData(char const * const interface, const RawBuffer &send, struct msghdr &hdr) { - int ret; - SockRAII sock; - ssize_t done = 0; - - if (SECURITY_SERVER_API_SUCCESS != (ret = sock.Connect(interface))) { - LogError("Error in SockRAII"); - return ret; - } - - while ((send.size() - done) > 0) { - if (0 >= waitForSocket(sock.Get(), POLLOUT, POLL_TIMEOUT)) { - LogError("Error in poll(POLLOUT)"); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - ssize_t temp = TEMP_FAILURE_RETRY(write(sock.Get(), &send[done], send.size() - done)); - if (-1 == temp) { - int err = errno; - LogError("Error in write: " << strerror(err)); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - done += temp; - } - - if (0 >= waitForSocket(sock.Get(), POLLIN, POLL_TIMEOUT)) { - LogError("Error in poll(POLLIN)"); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - ssize_t temp = TEMP_FAILURE_RETRY(recvmsg(sock.Get(), &hdr, MSG_CMSG_CLOEXEC)); - - if (temp < 0) { - int err = errno; - LogError("Error in recvmsg(): " << strerror(err) << " errno: " << err); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - if (0 == temp) { - LogError("Read return 0/Connection closed by server(?)"); - return SECURITY_SERVER_API_ERROR_SOCKET; - } - - return SECURITY_SERVER_API_SUCCESS; -} - -int try_catch(const std::function& func) -{ - try { - return func(); - } catch (MessageBuffer::Exception::Base &e) { - LogError("SecurityServer::MessageBuffer::Exception " << e.DumpToString()); - } catch (std::exception &e) { - LogError("STD exception " << e.what()); - } catch (...) { - LogError("Unknown exception occured"); - } - return SECURITY_SERVER_API_ERROR_UNKNOWN; -} - -} // namespace SecurityServer - -static void init_lib(void) __attribute__ ((constructor)); -static void init_lib(void) -{ - securityClientEnableLogSystem(); -} - -static void fini_lib(void) __attribute__ ((destructor)); -static void fini_lib(void) -{ - -} - diff --git a/src/common/exceptions/ServerConnectionErrorException.h b/src/common/exceptions/ServerConnectionErrorException.h new file mode 100644 index 0000000..ebdf574 --- /dev/null +++ b/src/common/exceptions/ServerConnectionErrorException.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file ServerConnectionErrorException.h + * @author Lukasz Wojciechowski + * @version 1.0 + * @brief Implementation of ServerConnectionErrorException + */ + +#ifndef SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_ +#define SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_ + +#include "Exception.h" + +#include + +namespace Cynara { + +class ServerConnectionErrorException : public Exception { +public: + ServerConnectionErrorException() = default; + virtual ~ServerConnectionErrorException() = default; + virtual const char* what() const noexcept { + return "ServerConnectionError"; + } +}; + +} /* namespace Cynara */ + +#endif /* SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_ */ -- 2.7.4