PageRenderTime 61ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/common/sockets/Socket.cpp

https://gitlab.com/admin-github-cloud/cynara
C++ | 291 lines | 220 code | 48 blank | 23 comment | 44 complexity | e00ecf4c22cbadb8340b7478c4828afb MD5 | raw file
  1. /*
  2. * Copyright (c) 2000 - 2016 Samsung Electronics Co., Ltd All Rights Reserved
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License
  15. */
  16. /**
  17. * @file src/common/sockets/Socket.cpp
  18. * @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
  19. * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
  20. * @version 1.0
  21. * @brief This file contains implementation of UNIX client socket class
  22. */
  23. #include <errno.h>
  24. #include <fcntl.h>
  25. #include <poll.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <sys/socket.h>
  29. #include <sys/types.h>
  30. #include <sys/un.h>
  31. #include <unistd.h>
  32. #include <exceptions/AccessDeniedException.h>
  33. #include <exceptions/InitException.h>
  34. #include <exceptions/NoMemoryException.h>
  35. #include <exceptions/UnexpectedErrorException.h>
  36. #include <log/log.h>
  37. #include "Socket.h"
  38. namespace Cynara {
  39. Socket::Socket(const std::string &socketPath, int timeoutMiliseconds)
  40. : m_sock(-1), m_connectionInProgress(false), m_socketPath(socketPath),
  41. m_pollTimeout(timeoutMiliseconds), m_sendBufferPos(0), m_sendBufferEnd(0) {
  42. }
  43. Socket::~Socket() {
  44. close();
  45. }
  46. void Socket::close(void) {
  47. if (m_sock > -1)
  48. ::close(m_sock);
  49. m_sock = -1;
  50. m_sendBufferPos = 0;
  51. m_sendBufferEnd = 0;
  52. m_sendQueue.clear();
  53. }
  54. bool Socket::waitForSocket(int event) {
  55. int ret;
  56. pollfd desc[1];
  57. desc[0].fd = m_sock;
  58. desc[0].events = event;
  59. if (event != POLLHUP)
  60. ret = TEMP_FAILURE_RETRY(poll(desc, 1, m_pollTimeout));
  61. else
  62. ret = TEMP_FAILURE_RETRY(poll(desc, 1, 0));
  63. if (ret == -1) {
  64. int err = errno;
  65. LOGE("'poll' function error [%d] : <%s>", err, strerror(err));
  66. throw UnexpectedErrorException(err, strerror(err));
  67. } else if (ret == 0) {
  68. LOGD("Poll timeout");
  69. }
  70. return (ret == 1);
  71. }
  72. int Socket::getSocketError(void) {
  73. int err = 0;
  74. socklen_t len = sizeof(err);
  75. int ret = getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &err, &len);
  76. if (ret < 0) {
  77. int err = errno;
  78. LOGE("'getsockopt' function error [%d] : <%s>", err, strerror(err));
  79. throw UnexpectedErrorException(err, strerror(err));
  80. }
  81. return err;
  82. }
  83. bool Socket::isConnected(void) {
  84. if (m_connectionInProgress)
  85. return true;
  86. if (m_sock < 0)
  87. return false;
  88. if (getSocketError() != 0)
  89. return false;
  90. return !waitForSocket(POLLHUP);
  91. }
  92. void Socket::createSocket(void) {
  93. int flags;
  94. m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
  95. if (m_sock < 0) {
  96. int err = errno;
  97. LOGE("'socket' function error [%d] : <%s>", err, strerror(err));
  98. throw UnexpectedErrorException(err, strerror(err));
  99. }
  100. if ((flags = fcntl(m_sock, F_GETFL, 0)) < 0 ||
  101. fcntl(m_sock, F_SETFL, flags | O_NONBLOCK) < 0)
  102. {
  103. int err = errno;
  104. close();
  105. LOGE("'fcntl' function error [%d] : <%s>", err, strerror(err));
  106. throw UnexpectedErrorException(err, strerror(err));
  107. }
  108. }
  109. Socket::ConnectionStatus Socket::connectSocket(void) {
  110. sockaddr_un clientAddr;
  111. memset(&clientAddr, 0, sizeof(clientAddr));
  112. clientAddr.sun_family = AF_UNIX;
  113. if (m_socketPath.length() >= sizeof(clientAddr.sun_path)) {
  114. close();
  115. LOGE("Error: socket path <%s> is too long [%zu]. Max len is [%zu]", m_socketPath.c_str(),
  116. m_socketPath.length(), sizeof(clientAddr.sun_path));
  117. throw InitException();
  118. }
  119. strcpy(clientAddr.sun_path, m_socketPath.c_str());
  120. LOGD("ClientAddr.sun_path <%s>", clientAddr.sun_path);
  121. int retval = TEMP_FAILURE_RETRY(::connect(m_sock, (struct sockaddr*)&clientAddr,
  122. SUN_LEN(&clientAddr)));
  123. if (retval == -1) {
  124. int err = errno;
  125. switch (err) {
  126. case EINPROGRESS:
  127. m_connectionInProgress = true;
  128. return ConnectionStatus::CONNECTION_IN_PROGRESS;
  129. case ECONNREFUSED:
  130. //no one is listening
  131. return ConnectionStatus::CONNECTION_FAILED;
  132. case EACCES:
  133. LOGE("Insufficient permissions to connect to socket at <%s>",
  134. m_socketPath.c_str());
  135. throw AccessDeniedException("Connecting to cynara socket <" +
  136. m_socketPath + ">");
  137. default:
  138. close();
  139. LOGE("'connect' function error [%d] : <%s>", err, strerror(err));
  140. throw UnexpectedErrorException(err, strerror(err));
  141. }
  142. }
  143. return ConnectionStatus::CONNECTION_SUCCEEDED;
  144. }
  145. Socket::SendStatus Socket::sendBuffer(void) {
  146. while (m_sendBufferEnd != m_sendBufferPos) {
  147. if (!waitForSocket(POLLOUT)) {
  148. LOGD("No POLLOUT event");
  149. return SendStatus::PARTIAL_DATA_SENT;
  150. }
  151. ssize_t t = TEMP_FAILURE_RETRY(send(m_sock, m_sendBuffer.data() + m_sendBufferPos,
  152. m_sendBufferEnd - m_sendBufferPos, MSG_NOSIGNAL));
  153. if (t == -1) {
  154. int err = errno;
  155. switch (err) {
  156. case EAGAIN:
  157. #if EWOULDBLOCK != EAGAIN
  158. case EWOULDBLOCK:
  159. #endif
  160. continue;
  161. case ENOMEM:
  162. throw NoMemoryException("'send' function failed due to ENOMEM");
  163. case EPIPE:
  164. LOGN("Connection closed by server");
  165. return SendStatus::CONNECTION_LOST;
  166. default:
  167. LOGE("'send' function error [%d] : <%s>", err, strerror(err));
  168. throw UnexpectedErrorException(err, strerror(err));
  169. }
  170. }
  171. m_sendBufferPos += static_cast<size_t>(t);
  172. }
  173. return SendStatus::ALL_DATA_SENT;
  174. }
  175. Socket::ConnectionStatus Socket::connect(void) {
  176. close();
  177. createSocket();
  178. ConnectionStatus status = connectSocket();
  179. if (status != ConnectionStatus::CONNECTION_SUCCEEDED)
  180. return status;
  181. return isConnected() ? ConnectionStatus::CONNECTION_SUCCEEDED
  182. : ConnectionStatus::CONNECTION_FAILED;
  183. }
  184. Socket::ConnectionStatus Socket::completeConnection(void) {
  185. if (!m_connectionInProgress)
  186. return ConnectionStatus::ALREADY_CONNECTED;
  187. if (!waitForSocket(POLLOUT))
  188. return ConnectionStatus::CONNECTION_IN_PROGRESS;
  189. m_connectionInProgress = false;
  190. return isConnected() ? ConnectionStatus::CONNECTION_SUCCEEDED
  191. : ConnectionStatus::CONNECTION_FAILED;
  192. }
  193. int Socket::getSockFd(void) {
  194. return m_sock;
  195. }
  196. bool Socket::isDataToSend(void) {
  197. return !m_sendQueue.empty() || m_sendBufferEnd != 0;
  198. }
  199. Socket::SendStatus Socket::sendToServer(BinaryQueue &queue) {
  200. m_sendQueue.appendMoveFrom(queue);
  201. SendStatus status = sendBuffer();
  202. if (status != SendStatus::ALL_DATA_SENT)
  203. return status;
  204. if (m_sendQueue.size() > m_sendBuffer.size())
  205. m_sendBuffer.resize(m_sendQueue.size());
  206. m_sendBufferEnd = m_sendQueue.size();
  207. m_sendBufferPos = 0;
  208. m_sendQueue.flattenConsume(m_sendBuffer.data(), m_sendQueue.size());
  209. return sendBuffer();
  210. }
  211. bool Socket::receiveFromServer(BinaryQueue &queue) {
  212. if (!waitForSocket(POLLIN)) {
  213. LOGD("No POLLIN event");
  214. return true;
  215. }
  216. RawBuffer buffer(BUFSIZ);
  217. ssize_t size = 0;
  218. while (true) {
  219. size = TEMP_FAILURE_RETRY(read(m_sock, buffer.data(), BUFSIZ));
  220. if (size == 0) {
  221. LOGW("read return 0 / Connection closed by server.");
  222. return false;
  223. }
  224. if (size == -1) {
  225. int err = errno;
  226. switch (err) {
  227. case EAGAIN:
  228. #if EWOULDBLOCK != EAGAIN
  229. case EWOULDBLOCK:
  230. #endif
  231. return true;
  232. case ECONNRESET:
  233. LOGW("read returned -1 with ECONNRESET / Connection closed by server.");
  234. return false;
  235. default:
  236. LOGE("'read' function error [%d] : <%s>", err, strerror(err));
  237. throw UnexpectedErrorException(err, strerror(err));
  238. }
  239. }
  240. queue.appendCopy(buffer.data(), static_cast<size_t>(size));
  241. }
  242. }
  243. } // namespace Cynara