PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/vio/TCP.cpp

https://gitlab.com/piconf/software
C++ | 308 lines | 240 code | 25 blank | 43 comment | 53 complexity | 68b12f886dca7cf10c78267340615d32 MD5 | raw file
Possible License(s): AGPL-3.0
  1. /******************************************************************************
  2. * This file is part of vio. *
  3. * *
  4. * Copyright (C) 2012, 2013, 2014 Rouven Spreckels <n3vu0r@nevux.org> *
  5. * *
  6. * vio is free software: you can redistribute it and/or modify *
  7. * it under the terms of the GNU Affero General Public License version 3 as *
  8. * published by the Free Software Foundation on 19 November 2007. *
  9. * *
  10. * vio is distributed in the hope that it will be useful, *
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  13. * GNU Affero General Public License for more details. *
  14. * *
  15. * You should have received a copy of the GNU Affero General Public License *
  16. * along with vio. If not, see <http://www.gnu.org/licenses/>. *
  17. ******************************************************************************/
  18. #ifndef _VIO_TCP_
  19. #define _VIO_TCP_
  20. #include "TCP.hpp"
  21. #include <cerrno>
  22. #include <cstring>
  23. #include <cstdlib>
  24. #include <unistd.h>
  25. void VIO::TCP::readable() {
  26. if (state & LISTENING) {
  27. if (state & ACCEPTING)
  28. accept();
  29. else
  30. state |= ACCEPTABLE;
  31. }
  32. else {
  33. //Data front = destinations.front();
  34. IO::readable();
  35. if (!fetched.size)
  36. state &= ~CONNECTED;
  37. //else
  38. //if (!front.size)
  39. // if (0 > recv(fd, front.data, fetched.size, 0))
  40. // state &= ~CONNECTED;
  41. }
  42. }
  43. void VIO::TCP::writable() {
  44. if (state & CONNECTING) {
  45. int value = 0;
  46. socklen_t size = sizeof value;
  47. if (0 > getsockopt(fd, SOL_SOCKET, SO_ERROR, &value, &size))
  48. throw Error("Cannot get socket options", std::strerror(errno), that, GETSOCKOPT);
  49. if (value) {
  50. ::close(fd);
  51. if (address)
  52. connecting();
  53. else
  54. throw Error("No addresses left", 0, that, UNAVAILABLE);
  55. }
  56. else {
  57. freeaddrinfo(address);
  58. configure();
  59. select &= ~WRITABLE;
  60. state &= ~CONNECTING;
  61. state |= CONNECTED;
  62. }
  63. }
  64. else {
  65. IO::writable();
  66. if (!pushed.size)
  67. state &= ~CONNECTED;
  68. }
  69. }
  70. void VIO::TCP::timewise() {
  71. static char byte;
  72. if (!isConnected())
  73. throw Error("Timer disconnected", std::strerror(errno), that, TIMER);
  74. if (!hasToRead()) {
  75. fetch(&byte, sizeof byte);
  76. clock_gettime(CLOCK_REALTIME, &last);
  77. select |= SCHEDULE;
  78. }
  79. else
  80. select &= ~SCHEDULE;
  81. }
  82. void VIO::TCP::timing(TCP* tcp, timespec span) {
  83. char byte;
  84. for (bool undone = true; undone; undone = process()) {
  85. if (!tcp->isConnected())
  86. break;
  87. if (!tcp->hasToWrite()) {
  88. timespec left = span;
  89. while (-1 == nanosleep(&left, &left) and errno == EINTR);
  90. tcp->push(&byte, sizeof byte);
  91. }
  92. }
  93. }
  94. /*
  95. void VIO::TCP::read() {
  96. int bytes = recv(fd, destinations.front().data, destinations.front().left, destinations.front().size ? 0 : MSG_PEEK);
  97. if (0 > bytes)
  98. bytes = 0;
  99. fetched.data = destinations.front().data;
  100. fetched.size = bytes;
  101. fetched.left = 0;
  102. }
  103. void VIO::TCP::write() {
  104. int bytes = send(fd, sources.front().data, sources.front().left, 0);
  105. if (0 > bytes)
  106. bytes = 0;
  107. pushed.data = sources.front().data;
  108. pushed.size = bytes;
  109. pushed.left = 0;
  110. }
  111. */
  112. void VIO::TCP::read() {
  113. try {
  114. IO::read();
  115. }
  116. catch (IO::Error& error) {
  117. state &= ~CONNECTED;
  118. //throw error;
  119. }
  120. }
  121. void VIO::TCP::write() {
  122. try {
  123. IO::write();
  124. }
  125. catch (IO::Error& error) {
  126. state &= ~CONNECTED;
  127. //throw error;
  128. }
  129. }
  130. void VIO::TCP::connecting() {
  131. if (!address)
  132. throw Error("No addresses left", 0, that, UNAVAILABLE);
  133. for (; address; address = address->ai_next) {
  134. if (0 > (fd = ::socket(address->ai_family, address->ai_socktype, address->ai_protocol)))
  135. continue;
  136. configure();
  137. if (::connect(fd, address->ai_addr, address->ai_addrlen) < 0) {
  138. if (errno == EINPROGRESS) {
  139. address = address->ai_next;
  140. select |= WRITABLE;
  141. break;
  142. }
  143. ::close(fd);
  144. continue;
  145. }
  146. freeaddrinfo(address);
  147. configure();
  148. select &= ~WRITABLE;
  149. state &= ~CONNECTING;
  150. state |= CONNECTED;
  151. break;
  152. }
  153. }
  154. void VIO::TCP::pair(TCP* tcp0, TCP* tcp1) {
  155. int fd[2];
  156. if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd))
  157. throw Error("Cannot create socketpair", std::strerror(errno), 0, SOCKETPAIR);
  158. tcp0->fd = fd[0];
  159. tcp1->fd = fd[1];
  160. tcp0->configure();
  161. tcp1->configure();
  162. tcp0->state |= CONNECTED;
  163. tcp1->state |= CONNECTED;
  164. }
  165. void VIO::TCP::forkTimer(TCP* pulse, timespec span) {
  166. TCP timer;
  167. TCP::pair(pulse, &timer);
  168. pulse->select |= TIMEWISE;
  169. pid_t pid;
  170. if (-1 == (pid = fork()))
  171. throw Error("Cannot fork process", std::strerror(errno), 0, FORK);
  172. if (pid) {
  173. timer.close();
  174. timer.clear();
  175. pulse->timewise();
  176. }
  177. else {
  178. pulse->close();
  179. pulse->clear();
  180. timing(&timer, span);
  181. exit(EXIT_SUCCESS);
  182. }
  183. }
  184. VIO::TCP::Error::Error(const char* context, const char* message, const void* instance, int code)
  185. : IO::Error(context, message, instance, code) {
  186. }
  187. VIO::TCP::TCP()
  188. : IO() {
  189. state = 0;
  190. address = 0;
  191. addrlen = sizeof (struct sockaddr);
  192. }
  193. void VIO::TCP::listen(std::string service, bool reuse, bool accepting) {
  194. int value = reuse ? 1 : 0;
  195. state |= LISTENING;
  196. if (accepting)
  197. state |= ACCEPTING;
  198. select |= READABLE;
  199. struct addrinfo hints, * address;
  200. memset(&hints, 0, sizeof hints);
  201. hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
  202. hints.ai_socktype = SOCK_STREAM;
  203. if (int gaino = ::getaddrinfo(0, service.c_str(), &hints, &address))
  204. throw Error("Cannot resolve address information", gai_strerror(gaino), that, GETADDRINFO);
  205. for (; address; address = address->ai_next) {
  206. if (0 > (fd = ::socket(address->ai_family, address->ai_socktype, address->ai_protocol)))
  207. continue;
  208. if (reuse and -1 == ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof (int)))
  209. throw Error("Cannot set socket option reuse", std::strerror(errno), that, SETSOCKOPT);
  210. if (::bind(fd, address->ai_addr, address->ai_addrlen) < 0) {
  211. ::close(fd);
  212. fd = -1;
  213. continue;
  214. }
  215. }
  216. freeaddrinfo(address);
  217. if (0 > fd)
  218. throw Error("Cannot create listening socket", std::strerror(errno), that, BIND);
  219. if (-1 == ::listen(fd, SOMAXCONN))
  220. throw Error("Cannot listen to socket", std::strerror(errno), that, LISTEN);
  221. configure();
  222. }
  223. VIO::TCP::Clients::reverse_iterator VIO::TCP::accept() {
  224. if (!(state & (ACCEPTING | ACCEPTABLE)))
  225. return clients.rend();
  226. TCP* client = new TCP();
  227. if (-1 == (client->fd = ::accept(fd, (struct sockaddr*)&client->addr, &client->addrlen)))
  228. return clients.rend();
  229. if (-1 == getpeername(client->fd, (struct sockaddr*)&client->addr, &client->addrlen))
  230. throw Error("Cannot get peer name", std::strerror(errno), that, GETPEERNAME);
  231. state &= ~ACCEPTABLE;
  232. client->state |= CONNECTED;
  233. client->select |= ACTIVITY;
  234. client->activity();
  235. client->configure();
  236. clients.push_back(client);
  237. return clients.rbegin();
  238. }
  239. void VIO::TCP::connect(std::string host, std::string service) {
  240. struct addrinfo hints;
  241. memset(&hints, 0, sizeof hints);
  242. hints.ai_flags = AI_ADDRCONFIG;
  243. hints.ai_socktype = SOCK_STREAM;
  244. if(int gaino = ::getaddrinfo(host.c_str(), service.c_str(), &hints, &address))
  245. throw Error("Cannot resolve address information", gai_strerror(gaino), that, GETADDRINFO);
  246. state |= CONNECTING;
  247. connecting();
  248. select |= ACTIVITY;
  249. activity();
  250. }
  251. bool VIO::TCP::isConnected() {
  252. return state & CONNECTED ? true : false;
  253. }
  254. bool VIO::TCP::isConnected(timespec timeout) {
  255. if (!IO::isActive(timeout)) {
  256. if (state & CONNECTED) {
  257. state &= ~CONNECTED;
  258. ::close(fd);
  259. clear();
  260. }
  261. activity();
  262. throw Error("Inactivity reached timeout", 0, that, TIMEOUT);
  263. }
  264. return isConnected();
  265. }
  266. std::string VIO::TCP::host() {
  267. char host[NI_MAXHOST];
  268. addrlen = sizeof (struct sockaddr_storage);
  269. if (int gaino = getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof host, NULL, 0, 0))
  270. throw Error("Cannot resolve name information", gai_strerror(gaino), that, GETNAMEINFO);
  271. return host;
  272. }
  273. void VIO::TCP::clear() {
  274. IO::clear();
  275. state &= LISTENING | ACCEPTING;
  276. }
  277. VIO::TCP::~TCP() {
  278. if (state & LISTENING)
  279. for (Clients::iterator client = clients.begin(); client != clients.end(); ++client)
  280. delete *client;
  281. }
  282. #endif