From: Bartlomiej Grzelewski Date: Fri, 24 Mar 2017 16:30:07 +0000 (+0100) Subject: Add ipc library for ask-user X-Git-Tag: submit/tizen/20170405.143506~30 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0b7f03c5d145e60ff0c74efd46c665a854fcfb26;p=platform%2Fcore%2Fsecurity%2Faskuser.git Add ipc library for ask-user This library is used in communication between privacy denied plugin/security manager and ask user service. Change-Id: If9e71a3a716095eba8b909cc28d08a1853ba2951 --- diff --git a/src/common/protocol/CMakeLists.txt b/src/common/protocol/CMakeLists.txt new file mode 100644 index 0000000..eef74f9 --- /dev/null +++ b/src/common/protocol/CMakeLists.txt @@ -0,0 +1,33 @@ +SET(TARGET_PROTOCOL "protocol") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -ggdb -O0") + +INCLUDE_DIRECTORIES( + ${PROJECT_SOURCE_DIR} + ) + +SET(PROTOCOL_SOURCES + ${PROJECT_SOURCE_DIR}/channel.cpp + ${PROJECT_SOURCE_DIR}/ask-user-client.cpp + ${PROJECT_SOURCE_DIR}/sock.cpp + ${PROJECT_SOURCE_DIR}/ask-user-config.cpp + ) + +ADD_LIBRARY(${TARGET_PROTOCOL} SHARED ${PROTOCOL_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_PROTOCOL}) + + +SET(TARGET_PTEST "test") + +SET(TARGET_PTEST_SOURCES + ${PROJECT_SOURCE_DIR}/main.cpp + ) + +ADD_EXECUTABLE(${TARGET_PTEST} ${TARGET_PTEST_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_PTEST} ${TARGET_PROTOCOL}) + + + + diff --git a/src/common/protocol/ask-user-client.cpp b/src/common/protocol/ask-user-client.cpp new file mode 100644 index 0000000..4c77db9 --- /dev/null +++ b/src/common/protocol/ask-user-client.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 ipc-client.cpp + * @author Bartlomiej Grzelewski + * @brief + */ +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace AskUser { +namespace Protocol { + +int popup_launch(const std::string &pkgName, + const std::string &appName, + uid_t uid, + const PrivilegeVector &privileges, + int &result) +{ + try { + std::string path = SOCKET_STREAM_PATH + "." + std::to_string(uid); + Sock s(Sock::CLI_STREAM); + if (0 > s.connect(path)) + return -1; + + std::stringstream ss; + ss << MSGID_POPUP << " " << pkgName << " " << appName << " " << uid; + for (auto &e : privileges) { + ss << " " << e; + } + + std::string str = ss.str(); + + if (0 > s.send(RawBuffer(str.begin(), str.end()))) + return -1; + + RawBuffer resp; + if (0 > s.wait(FdMask::READ)) + return -1; + if (0 > s.recv(resp)) + return -1; + + std::string input(resp.begin(), resp.end()); + std::stringstream sss(input); + sss >> result; + + return 0; + } catch (const std::exception &) { + return -1; + } +} + +int popup_runtime(const std::string &pkgName, + const std::string &appName, + uid_t uid, + std::string &privilege, + int &result) +{ + return -1; +} + + +int toast_deny(const std::string &pkgName, + const std::string &appName, + uid_t uid, + const std::string &privilege) +{ + try { + std::string path = SOCKET_DGRAM_PATH + "." + std::to_string(uid); + Sock s(Sock::CLI_DGRAM); + if (0 > s.connect(path)) + return -1; + + std::string str = std::to_string(MSGID_TOAST1) + " " + pkgName + " " + appName + " " + std::to_string(uid) + " " + privilege; + + return s.send(RawBuffer(str.begin(), str.end())); + } catch (const std::exception &) { + return -1; + } +} + +int toast_fail_launch(const std::string &pkgName, const std::string &appName, uid_t uid) { + try { + std::string path = SOCKET_DGRAM_PATH + "." + std::to_string(uid); + Sock s(Sock::CLI_DGRAM); + if (0 > s.connect(path)) + return -1; + + std::string str = std::to_string(MSGID_TOAST2) + " " + pkgName + " " + appName + " " + std::to_string(uid); + + return s.send(RawBuffer(str.begin(), str.end())); + } catch (const std::exception &) { + return -1; + } +} + +} // namespace Protocol +} // namespace AskUser + + diff --git a/src/common/protocol/ask-user-config.cpp b/src/common/protocol/ask-user-config.cpp new file mode 100644 index 0000000..415f1a5 --- /dev/null +++ b/src/common/protocol/ask-user-config.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 ask-user-config.cpp + * @author Bartlomiej Grzelewski + * @brief + */ +#include + +const std::string SOCKET_STREAM_PATH("/tmp/ask-user-stream"); +const std::string SOCKET_DGRAM_PATH("/tmp/ask-user-dgram"); + +//const int MSGID_POPUP = 1; +//const int MSGID_TOUST1 = 2; +//const int MSGID_TOUST2 = 3; +// diff --git a/src/common/protocol/ask-user-config.h b/src/common/protocol/ask-user-config.h new file mode 100644 index 0000000..0205bb3 --- /dev/null +++ b/src/common/protocol/ask-user-config.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 ask-user-config.h + * @author Bartlomiej Grzelewski + * @brief + */ +#pragma once + +#include + +extern const std::string SOCKET_STREAM_PATH; +extern const std::string SOCKET_DGRAM_PATH; + +// We want to use it constat expressions (this is the reason why it's not in cpp file +const int MSGID_POPUP = 1; +const int MSGID_TOAST1 = 2; +const int MSGID_TOAST2 = 3; + diff --git a/src/common/protocol/ask-user/ask-user-client.h b/src/common/protocol/ask-user/ask-user-client.h new file mode 100644 index 0000000..15b9e44 --- /dev/null +++ b/src/common/protocol/ask-user/ask-user-client.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 ask-user-client.h + * @author Bartlomiej Grzelewski + * @brief + */ +#pragma once + +#include + +#include +#include +#include +#include + +namespace AskUser { +namespace Protocol { +/** + * Synchronous request for showing popup. + * + * \param[in] pkgName Application package name. + * \param[in] appName Application name. + * \param[in] uid Information about user that should see popup. + * \param[in] privileges List of privileges that should be shown to user. + * \param[out] result Result returned by ask-user application. + * + * \return Value less that 0 means ipc error. + */ + int popup_launch(const std::string &pkgName, const std::string &appName, uid_t uid, const PrivilegeVector &privileges, int &result); + + /** + * Synchronous request for showing popup. + * + * \param[in] pkgName Application package name. + * \param[in] appName Application name. + * \param[in] uid Information about user that should see popup. + * \param[in] privilege Privilege that should be shown to user. + * \param[out] result Result returned by ask-user application. + * + * \return Value less that 0 means ipc error. + */ + int popup_runtime(const std::string &pkgName, const std::string &appName, uid_t uid, std::string &privilege, int &result); + +/** + * Nonblocking request for showing toast. + * + * \param[in] pkgName Application package name. + * \param[in] appName Application name. + * \param[in] uid Information about user that should see popup. + * \param[in] privilege Name of privilege that was denied. + * + * \return Value less that 0 means ipc error. + */ +int toast_deny(const std::string &pkgName, const std::string &appName, uid_t uid, const std::string &privilege); + +/** + * Nonblocking request for showing toast. + * + * \param[in] pkgName Application package name. + * \param[in] appName Application name. + * \param[in] uid Information about user that should see popup. + * + * \return Value less that 0 means ipc error. + */ +int toast_fail_launch(const std::string &pkgName, const std::string &appName, uid_t uid); + +} // namespace Protocol +} // namespace AskUser + diff --git a/src/common/protocol/ask-user/ask-user-service.h b/src/common/protocol/ask-user/ask-user-service.h new file mode 100644 index 0000000..ca9b704 --- /dev/null +++ b/src/common/protocol/ask-user/ask-user-service.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 ask-user-service.h + * @author Bartlomiej Grzelewski + * @brief + */ +#pragma once + +#include + +#include +#include +#include + +#include + +namespace AskUser { +namespace Protocol { + +enum FdMask { + READ = 1, + WRITE = 2, +}; + +/** + * IServiceCallbacks defines set of callbacks that must be implemented in service. + */ + +struct IServerCallbacks { + + virtual ~IServerCallbacks(){} + /** + * This function gives you number of descriptor that should be watched by poll/select. + * + * \param[in] fd Number of opened descriptor. + * \param[in] mask Type of acction that is required on this descriptor. + * mask == 0 remove descriptor fd from watched pool + * mask == 1 watch descriptor for READ + * mask == 2 watch descriptor for WRITE + * maks == 3 watch descriptor for READ and WRITE + */ + virtual void updateFd(int fd, int mask) = 0; + /** + * some client called popup function with parameters. + */ + virtual void popup_launch(int requestId, const std::string &pkgName, const std::string &appName, uid_t uid, const PrivilegeVector &privileges) = 0; + /** + * some client called test_deny function with parameters. + */ + virtual void toast_deny(const std::string &pkgName, const std::string &appName, uid_t uid, const Privilege &privilege) = 0; + /** + * some client called toast_fail_launch function with parameters. + */ + virtual void toast_fail_launch(const std::string &pkgName, const std::string &appName, uid_t uid) = 0; +}; + +struct IChannel { + virtual ~IChannel(){} + + /** + * Process function should be called each time some event is reported by poll/select on + * descriptor. + * + * \param[in] fd Number of descriptor. + * \param[in] mask Information about event that is waiting on descriptor + * (FdMask::READ or FdMask::WRITE). If you pass 0 for some reason + * the descriptor will be closed and callback updateFd will be called + * with mask = 0 + */ + virtual void process(int fd, int mask) = 0; + + /** + * Information about action that was choosed by user. + * + * \param[in] requestId Request number. + * \param[in] response Information about action choosed by user. + */ + virtual void popupResponse(int requestId, int response) = 0; +}; + +typedef std::unique_ptr ChannelPtr; +typedef std::unique_ptr ServerCallbacksPtr; + +ChannelPtr createChannel(ServerCallbacksPtr ptr); + +} // namespace Protocol +} // namespace AskUser + diff --git a/src/common/protocol/ask-user/ask-user-types.h b/src/common/protocol/ask-user/ask-user-types.h new file mode 100644 index 0000000..642667c --- /dev/null +++ b/src/common/protocol/ask-user/ask-user-types.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 ask-user-types.h + * @author Bartlomiej Grzelewski + * @brief + */ +#pragma once + +#define ASKUSER_DENY_ONCE 0 +#define ASKUSER_DENY_FOREVER 1 +#define ASKUSER_ALLOW_ONCE 2 +#define ASKUSER_ALLOW_FOREVER 3 +#define ASKUSER_UNKNOWN_ERROR -255 + +namespace AskUser { +namespace Protocol { + +typedef std::string Privilege; +typedef std::vector PrivilegeVector; + +} // namespace Protocol +} // AskUser + diff --git a/src/common/protocol/channel.cpp b/src/common/protocol/channel.cpp new file mode 100644 index 0000000..7b7e726 --- /dev/null +++ b/src/common/protocol/channel.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 channel.cpp + * @author Bartlomiej Grzelewski + * @brief + */ +#include +#include + +#include + +#include +#include +#include + +namespace AskUser { +namespace Protocol { + +void Channel::init() { + Sock stream(Sock::SRV_STREAM); + Sock datagram(Sock::SRV_DGRAM); + + uid_t uid = getuid(); + std::string path = SOCKET_STREAM_PATH + "." + std::to_string(uid); + stream.connect(path); + path = SOCKET_DGRAM_PATH + "." + std::to_string(uid); + datagram.connect(path); + + int fd1 = stream.getFd(); + int fd2 = datagram.getFd(); + + m_socket[fd1] = SockDesc(std::move(stream)); + m_socket[fd2] = SockDesc(std::move(datagram)); + + m_callbacks->updateFd(fd1, FdMask::READ); + m_callbacks->updateFd(fd2, FdMask::READ); +} + +void Channel::process(int fd, int mask) { + try { + auto it = m_socket.find(fd); + if (it == m_socket.end()) + return; + + if (0 == mask) { + m_socket.erase(it); + m_callbacks->updateFd(fd, 0); + return; + } + + auto &desc = it->second; + + if (desc.sock.getType() == Sock::SRV_STREAM) { + Sock client = desc.sock.accept(); + int fd = client.getFd(); + if (fd < 0) + return; + m_socket[fd] = SockDesc(std::move(client)); + m_callbacks->updateFd(fd, FdMask::READ); + return; + } + + if (mask & FdMask::READ) { + int ret = desc.sock.recv(desc.input); + + if (ret <= 0) { + m_socket.erase(fd); + m_callbacks->updateFd(fd, 0); + return; + } + + std::string data(desc.input.begin(), desc.input.end()); + desc.input.clear(); + std::stringstream ss(data); + + int command; + std::string appId, pkgName; + uid_t uid; + + ss >> command >> pkgName >> appId >> uid; + + switch(command) { + case MSGID_POPUP: + { + PrivilegeVector vect; + std::string privilege; + while(ss) { + ss >> privilege; + vect.push_back(privilege); + } + m_callbacks->popup_launch(fd, pkgName, appId, uid, vect); + break; + } + case MSGID_TOAST1: + { + std::string privilege; + ss >> privilege; + m_callbacks->toast_deny(pkgName, appId, uid, privilege); + break; + } + case MSGID_TOAST2: + { + m_callbacks->toast_fail_launch(pkgName, appId, uid); + break; + } + } + } + + if (mask & FdMask::WRITE) { + int size = static_cast(desc.output.size()); + int result = desc.sock.send(desc.output); + if (result < 0) { + m_socket.erase(fd); + m_callbacks->updateFd(fd, 0); + return; + } + + if (result == size) { + desc.output.clear(); + m_callbacks->updateFd(fd, FdMask::READ); + } + + if (result < size) { + desc.output.erase(desc.output.begin(), desc.output.begin()+result); + } + } + } catch (const std::exception &){} +} + +void Channel::popupResponse(int requestId, int response) { + try { + auto it = m_socket.find(requestId); + if (it == m_socket.end()) + return; + + auto &desc = it->second; + + std::stringstream ss; + ss << response; + std::string o = ss.str(); + + std::copy(o.begin(), o.end(), std::back_inserter(desc.output)); + m_callbacks->updateFd(requestId, FdMask::READ | FdMask::WRITE); + } catch (const std::exception &){} +} + +Channel::~Channel() { + for (auto &e : m_socket) + m_callbacks->updateFd(e.first, 0); +} + +ChannelPtr createChannel(ServerCallbacksPtr ptr) { + try { + Channel *c = new Channel(std::move(ptr)); + c->init(); + return ChannelPtr(c); + } catch (const std::exception &) { + return ChannelPtr(nullptr); + } +} + +} // namespace Protocol +} // namespace AskUser + diff --git a/src/common/protocol/channel.h b/src/common/protocol/channel.h new file mode 100644 index 0000000..8898155 --- /dev/null +++ b/src/common/protocol/channel.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 channel.h + * @author Bartlomiej Grzelewski + * @brief + */ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + + +namespace AskUser { +namespace Protocol { + +struct SockDesc { + SockDesc(Sock psock) + : sock(std::move(psock)) + {} + SockDesc(){} + Sock sock; + RawBuffer input; + RawBuffer output; +}; + +typedef std::map SocketMap; + +class Channel : public IChannel { +public: + Channel(ServerCallbacksPtr ptr) + : m_callbacks(std::move(ptr)) + {} + + void init(); + virtual void process(int fd, int mask); + virtual void popupResponse(int requestId, int response); + virtual ~Channel(); + +private: + ServerCallbacksPtr m_callbacks; + SocketMap m_socket; +}; + +} // namespace Protocol +} // namespace AskUser + diff --git a/src/common/protocol/main.cpp b/src/common/protocol/main.cpp new file mode 100644 index 0000000..d733494 --- /dev/null +++ b/src/common/protocol/main.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 main.cpp + * @author Bartlomiej Grzelewski + * @brief + */ +#include + +#include +#include + +#include +#include + +std::map m_sockets; + +using namespace AskUser::Protocol; + +struct Callbacks : public IServerCallbacks { + Callbacks() : m_channel(nullptr) {} + + virtual void updateFd(int fd, int mask) { + printf("call updateFd %d %d\n", fd, mask); + if (mask == 0) { + m_sockets.erase(fd); + return; + } + m_sockets[fd] = mask; + } + + virtual void popup_launch(int requestId, const std::string &pkgName, const std::string &appName, uid_t uid, const PrivilegeVector &priv) { + printf("call popup %s %s %d\n", pkgName.c_str(), appName.c_str(), uid); + if (m_channel) + m_channel->popupResponse(requestId, 0xdeadbeef); + } + + virtual void toast_deny(const std::string &pkgName, const std::string &appName, uid_t uid, const Privilege &privilege) { + printf("call toast_deny %s %s %d\n", pkgName.c_str(), appName.c_str(), uid); + } + + virtual void toast_fail_launch(const std::string &pkgName, const std::string &appName, uid_t uid) { + printf("call toast_fail_launch %s %s %d\n", pkgName.c_str(), appName.c_str(), uid); + } + + void setChannel(IChannel *ptr) { + m_channel = ptr; + } + +private: + IChannel *m_channel; +}; + +void server(void) { + Callbacks *c = new Callbacks; + ChannelPtr ptr = createChannel(ServerCallbacksPtr(c)); + c->setChannel(ptr.get()); + + pollfd fd[100]; + + while(1) { + int last = 0; + for (auto &e : m_sockets) { + fd[last].fd = e.first; + fd[last].revents = 0; + fd[last].events = ((e.second & FdMask::READ) ? POLLIN : 0) | ((e.second & FdMask::WRITE) ? POLLOUT : 0); + last++; + } + if (-1 == poll(fd, last, -1)) { + printf("Error in poll. Quit\n"); + return; + } + for (int i=0; iprocess(fd[i].fd, FdMask::READ); + if (fd[i].revents & POLLOUT) + ptr->process(fd[i].fd, FdMask::WRITE); + } + } +} + +void stream() { + PrivilegeVector vect = {"one", "two"}; + int result; + int ret = popup_launch("spkg", "sapp", getuid(), vect, result); + printf("Sended stream. Result: %x\n", result); +} + +void toust1() { + toast_deny("tpkg1", "sapp1", getuid(), "some_priv"); + printf("sended\n"); +} + +void toust2() { + toast_fail_launch("tpkg2", "sapp2", getuid()); + printf("sended\n"); +} + +int main(){ + int com; + printf("0 - server, 1 - send stream, 2 - send toust1, 3 - send toust2\n>"); + scanf("%d", &com); + + switch(com) { + case 0: + server(); + break; + case 1: + stream(); + break; + case 2: + toust1(); + break; + case 3: + toust2(); + break; + } + + return 0; +} diff --git a/src/common/protocol/raw-buffer.h b/src/common/protocol/raw-buffer.h new file mode 100644 index 0000000..8c2c779 --- /dev/null +++ b/src/common/protocol/raw-buffer.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 raw-buffer.h + * @author Bartlomiej Grzelewski + * @brief + */ +#pragma once + +#include + +namespace AskUser { +namespace Protocol { + +typedef std::vector RawBuffer; + +} // namespace Protocol +} // namespace AskUser + diff --git a/src/common/protocol/sock.cpp b/src/common/protocol/sock.cpp new file mode 100644 index 0000000..cde563a --- /dev/null +++ b/src/common/protocol/sock.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 Sock.cpp + * @author Bartlomiej Grzelewski + * @brief Implementation of Sock methods + */ +#include +#include +#include +#include +#include + +#ifdef WITH_SYSTEMD +#include +#endif // WITH_SYSTEMD + +#include +#include + +#include + +#include + +namespace { + +int getSocketFromSystemD() { +#ifdef WITH_SYSTEMD + int n = sd_listen_fds(0); + + if (n < 0) + return -1; + + for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START+n; ++fd) + if (0 < sd_is_socket_unix(fd, getUnixSockType, 1, m_path.c_str(), 0)) + return fd; +#endif // WITH_SYSTEMD + return -1; +} + +} // namespace Anonymous + +namespace AskUser { +namespace Protocol { + +Sock::Sock(Sock &&second) + : m_type(second.m_type) + , m_fd(second.m_fd) +{ + second.m_fd = -1; +} + +Sock::Sock(Sock::Type type, int fd) + : m_type(type) + , m_fd(fd) +{} + +Sock& Sock::operator=(Sock &&second) { + if (this == &second) + return *this; + + close(); + + m_fd = second.m_fd; + m_type = second.m_type; + + second.m_fd = -1; + + return *this; +} + + +int Sock::getUnixSockType() const { + switch(m_type) { + case SRV_DGRAM: + case CLI_DGRAM: + return SOCK_DGRAM; + case SRV_STREAM: + case CLI_STREAM: + return SOCK_STREAM; + } + return 0; +} + +int Sock::connect(const std::string &path) { + if (m_fd != -1) + return -1; + + m_path = path; + + bool policySystemD = true; + bool policyUnlink = true; + bool policySocket = true; + bool policyBind = true; + bool policyListen = true; + bool policyConnect = true; + + switch(m_type) { + case SRV_STREAM: + policyConnect = false; + break; + case CLI_STREAM: + policySystemD = false; + policyUnlink = false; + policyBind = false; + policyListen = false; + break; + case SRV_DGRAM: + policyListen = false; + policyConnect = false; + break; + case CLI_DGRAM: + policySystemD = false; + policyUnlink = false; + policyBind = false; + policyListen = false; + policyConnect = false; + break; + } + + if (m_fd != -1) { + return -1; + } + + if (policySystemD) { + m_fd = getSocketFromSystemD(); + if (m_fd >= 0) { + policyUnlink = false; + policySocket = false; + policyBind = false; + } + } + + if (policyUnlink) + ::unlink(m_path.c_str()); // we ignore return value by design + + if (policySocket) + m_fd = ::socket(AF_UNIX, getUnixSockType(), 0); + + if (m_fd < 0) + return -1; + + // remote is used in bind and in connect + sockaddr_un remote; + auto length = sizeof(sockaddr_un); + if (policyBind || policyConnect) { + remote.sun_family = AF_UNIX; + if (path.size() >= sizeof(remote.sun_path)) { + close(); + return -1; + } + memcpy(remote.sun_path, path.c_str(), path.size()+1); + } + + if (policyBind && (-1 == ::bind(m_fd, reinterpret_cast(&remote), sizeof(remote)))) { + close(); + return -1; + } + + if (policyListen && (-1 == ::listen(m_fd, 5))) { + close(); + return -1; + } + + if (policyConnect && (-1 == TEMP_FAILURE_RETRY(::connect(m_fd, reinterpret_cast(&remote), static_cast(length))))) + { + close(); + return -1; + } + return 0; +} + +Sock Sock::accept() { + int retFd = TEMP_FAILURE_RETRY(::accept(m_fd, nullptr, nullptr)); + if (retFd < 0) { + return Sock(CLI_STREAM, -1); + } + return Sock(CLI_STREAM, retFd); +} + +int Sock::send(const RawBuffer &buffer) { + static const int flags = MSG_NOSIGNAL | MSG_DONTWAIT; + if (m_fd < 0) + return -1; + + switch(m_type) { + default: + return -1; + case CLI_STREAM: + { + return static_cast( + TEMP_FAILURE_RETRY(::send(m_fd, buffer.data(), buffer.size(), flags))); + } + case CLI_DGRAM: + { + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, m_path.data(), m_path.size()); + return static_cast( + TEMP_FAILURE_RETRY(::sendto(m_fd, buffer.data(), buffer.size(), flags, + reinterpret_cast(&addr), sizeof(addr)))); + } + } +} + +int Sock::wait(int mask) { + pollfd fd = {}; + + fd.fd = m_fd; + fd.events = (mask & FdMask::READ ? POLLIN : 0) | (mask & FdMask::WRITE ? POLLOUT : 0); + + if (fd.events == 0) + return -1; + + if (0 > ::poll(&fd, 1, -1)) + return -1; + return 0; +} + +int Sock::recv(RawBuffer &output) { + if (m_fd < 0) + return -1; + + switch(m_type) { + default: + return -1; + case CLI_STREAM: + case SRV_DGRAM: + { + RawBuffer buffer(4096); + int result = TEMP_FAILURE_RETRY(::recv(m_fd, buffer.data(), buffer.size(), MSG_DONTWAIT)); + if (result > 0) + std::copy(buffer.begin(), buffer.begin()+result, std::back_inserter(output)); + return static_cast(result); + } + } +} + +void Sock::close() { + if (m_fd >= 0) + ::close(m_fd); + m_fd = -1; +} + +} // namespace Protocol +} // namespace AskUser + diff --git a/src/common/protocol/sock.h b/src/common/protocol/sock.h new file mode 100644 index 0000000..499bf1c --- /dev/null +++ b/src/common/protocol/sock.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co. + * + * 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 Sock.cpp + * @author Bartlomiej Grzelewski + * @brief Declaration of client/server Sock wrapper + */ + +#pragma once + +#include +#include + +#include + +namespace AskUser { +namespace Protocol { + +class Sock { +public: + enum Type { + SRV_STREAM, // server side STREAM socket (you may call accept on it) + // accept will always return CLI_STREAM + CLI_STREAM, // client side STREAM socket + SRV_DGRAM, // server side DATAGRAM socket + CLI_DGRAM, // client side DATAGRAM socket + }; + + Sock(Type type = SRV_STREAM, int fd = -1); + + Sock(const Sock &other) = delete; + Sock &operator=(const Sock &other) = delete; + + Sock(Sock &&other); + Sock &operator=(Sock &&other); + + virtual ~Sock() { close(); } + + int getFd() const { return m_fd; } + int getType() const { return m_type; } + int getUnixSockType() const; + + + int connect(const std::string &path); + Sock accept(); + void close(); + + int wait(int mask); + int recv(RawBuffer &buffer); + int send(const RawBuffer &buffer); + +private: + Type m_type; + int m_fd; + std::string m_path; +}; + +} // namespace Protocol +} // namespace AskUser +