libtasks Documentation  1.6
socket.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2014 ADTECH GmbH
3  * Licensed under MIT (https://github.com/adtechlabs/libtasks/blob/master/COPYING)
4  *
5  * Author: Andreas Pohl
6  */
7 
8 #include <arpa/inet.h>
9 #include <cstring>
10 #include <fcntl.h>
11 #include <netdb.h>
12 #include <netinet/in.h>
13 #include <sstream>
14 #include <sys/stat.h>
15 #include <sys/un.h>
16 #include <tasks/logging.h>
17 #include <tasks/net/socket.h>
18 #include <unistd.h>
19 
20 namespace tasks {
21 namespace net {
22 
23 socket::socket(socket_type type) : io_base(), m_type(type) {
24  if (socket_type::UDP == m_type) {
25  m_fd = ::socket(AF_INET, SOCK_DGRAM, 0);
26  if (m_fd < 0) {
27  throw tasks_exception(tasks_error::SOCKET_SOCKET, "socket failed: " + std::string(std::strerror(errno)),
28  errno);
29  }
30  } else if (socket_type::TCP != m_type) {
31  terr("socket: Invalid socket_type! Using TCP.");
33  }
34 }
35 
36 void socket::listen(std::string path, int queue_size) {
37  m_fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
38  if (m_fd < 0) {
39  throw tasks_exception(tasks_error::SOCKET_SOCKET, "socket failed: " + std::string(std::strerror(errno)), errno);
40  }
41 #ifndef _OS_LINUX_
42  int on = 1;
43  if (setsockopt(m_fd, SOL_SOCKET, SO_NOSIGPIPE, (char *)&on, sizeof(on))) {
45  "setsockopt SO_NOSIGPIPE failed: " + std::string(std::strerror(errno)), errno);
46  }
47 #endif
48  if (!m_blocking) {
49  if (fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL, 0) | O_NONBLOCK)) {
50  throw tasks_exception(tasks_error::SOCKET_FNCTL, "fcntl failed: " + std::string(std::strerror(errno)),
51  errno);
52  }
53  }
54  struct sockaddr_un addr;
55  bzero(&addr, sizeof(struct sockaddr_un));
56  addr.sun_family = AF_UNIX;
57  std::strcpy(addr.sun_path, path.c_str());
58  unlink(addr.sun_path);
59 #ifdef _OS_LINUX_
60  if (::bind(m_fd, (struct sockaddr *)&addr, sizeof(addr.sun_family) + path.length())) {
61  throw tasks_exception(tasks_error::SOCKET_BIND, "bind failed: " + std::string(std::strerror(errno)), errno);
62  }
63 #else
64  if (::bind(m_fd, (struct sockaddr *)&addr, SUN_LEN(&addr))) {
65  throw tasks_exception(tasks_error::SOCKET_BIND, "bind failed: " + std::string(std::strerror(errno)), errno);
66  }
67 #endif
68  if (chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) {
69  throw tasks_exception(tasks_error::SOCKET_CHMOD, "chmod failed: " + std::string(std::strerror(errno)), errno);
70  }
71  if (::listen(m_fd, queue_size) != 0) {
72  throw tasks_exception(tasks_error::SOCKET_LISTEN, "listen failed: " + std::string(std::strerror(errno)), errno);
73  }
74 }
75 
76 void socket::listen(int port, std::string ip, int queue_size) {
77  if (udp()) {
78  throw tasks_exception(tasks_error::SOCKET_LISTEN_UDP, "listen failed: can't be called for UDP sockets");
79  }
80  bind(port, ip, false /* mark this object as tcp socket */);
81  if (::listen(m_fd, queue_size)) {
82  throw tasks_exception(tasks_error::SOCKET_LISTEN, "listen failed: " + std::string(std::strerror(errno)), errno);
83  }
84 }
85 
86 void socket::bind(int port, std::string ip) {
87  bind(port, ip, true /* mark this object as udp socket */);
88 }
89 
90 void socket::bind(int port, std::string ip, bool udp) {
91  int on = 1;
93  m_fd = ::socket(AF_INET, udp ? SOCK_DGRAM : SOCK_STREAM, 0);
94  if (m_fd < 0) {
95  throw tasks_exception(tasks_error::SOCKET_SOCKET, "socket failed: " + std::string(std::strerror(errno)), errno);
96  }
97  if (setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) {
99  "setsockopt SO_REUSEADDR failed: " + std::string(std::strerror(errno)), errno);
100  }
101 #ifndef _OS_LINUX_
102  if (setsockopt(m_fd, SOL_SOCKET, SO_NOSIGPIPE, (char *)&on, sizeof(on))) {
104  "setsockopt SO_NOSIGPIPE failed: " + std::string(std::strerror(errno)), errno);
105  }
106 #endif
107  if (!m_blocking) {
108  if (fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL, 0) | O_NONBLOCK)) {
109  throw tasks_exception(tasks_error::SOCKET_FNCTL, "fcntl failed: " + std::string(std::strerror(errno)),
110  errno);
111  }
112  }
113  init_sockaddr(port, ip);
114  if (::bind(m_fd, (struct sockaddr *)m_addr.get(), sizeof(*(m_addr.get())))) {
115  throw tasks_exception(tasks_error::SOCKET_BIND, "bind failed: " + std::string(std::strerror(errno)), errno);
116  }
117 }
118 
119 void socket::init_sockaddr(int port, std::string ip) {
120  if (nullptr == m_addr) {
121  m_addr = std::make_shared<struct sockaddr_in>();
122  }
123  bzero(m_addr.get(), sizeof(struct sockaddr_in));
124  m_addr->sin_family = AF_INET;
125  if (ip.length()) {
126  if (inet_pton(AF_INET, ip.c_str(), &(m_addr->sin_addr)) < 1) {
127  terr("socket: Invalid ip " << ip << "! Binding to 0.0.0.0!" << std::endl);
128  m_addr->sin_addr.s_addr = INADDR_ANY;
129  }
130  } else {
131  m_addr->sin_addr.s_addr = INADDR_ANY;
132  }
133  m_addr->sin_port = htons(port);
134 }
135 
137  struct sockaddr_in addr;
138  bzero(&addr, sizeof(struct sockaddr_in));
139  socklen_t len = sizeof(addr);
140  int client = ::accept(m_fd, (struct sockaddr *)&addr, &len);
141  if (client < 0) {
142  throw tasks_exception(tasks_error::SOCKET_ACCEPT, "accept failed: " + std::string(std::strerror(errno)), errno);
143  }
144  return socket(client);
145 }
146 
147 void socket::connect(std::string path) {
148  m_fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
149  if (m_fd < 0) {
150  throw tasks_exception(tasks_error::SOCKET_SOCKET, "socket failed: " + std::string(std::strerror(errno)), errno);
151  }
152 #ifndef _OS_LINUX_
153  int on = 1;
154  if (setsockopt(m_fd, SOL_SOCKET, SO_NOSIGPIPE, (char *)&on, sizeof(on))) {
156  "setsockopt SO_NOSIGPIPE failed: " + std::string(std::strerror(errno)), errno);
157  }
158 #endif
159  struct sockaddr_un addr;
160  bzero(&addr, sizeof(struct sockaddr_un));
161  addr.sun_family = AF_UNIX;
162  std::strcpy(addr.sun_path, path.c_str());
163 
164 #ifdef _OS_LINUX_
165  if (::connect(m_fd, (struct sockaddr *)&addr, sizeof(addr.sun_family) + path.length())) {
166  throw tasks_exception(tasks_error::SOCKET_CONNECT, "connect failed: " + std::string(std::strerror(errno)),
167  errno);
168  }
169 #else
170  addr.sun_len = SUN_LEN(&addr);
171  if (::connect(m_fd, (struct sockaddr *)&addr, SUN_LEN(&addr))) {
172  throw tasks_exception(tasks_error::SOCKET_CONNECT, "connect failed: " + std::string(std::strerror(errno)),
173  errno);
174  }
175 #endif
176 
177  if (!m_blocking) {
178  if (fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL, 0) | O_NONBLOCK)) {
179  throw tasks_exception(tasks_error::SOCKET_FNCTL, "fcntl failed: " + std::string(std::strerror(errno)),
180  errno);
181  }
182  }
183 }
184 
185 void socket::connect(std::string host, int port) {
186  struct hostent *remote = gethostbyname(host.c_str());
187  if (nullptr == remote) {
188  throw tasks_exception(tasks_error::SOCKET_NOHOST, "Host " + host + " not found");
189  }
190  m_fd = ::socket(AF_INET, SOCK_STREAM, 0);
191  if (m_fd < 0) {
192  throw tasks_exception(tasks_error::SOCKET_SOCKET, "socket failed: " + std::string(std::strerror(errno)), errno);
193  }
194 #ifndef _OS_LINUX_
195  int on = 1;
196  if (setsockopt(m_fd, SOL_SOCKET, SO_NOSIGPIPE, (char *)&on, sizeof(on))) {
198  "setsockopt SO_NOSIGPIPE failed: " + std::string(std::strerror(errno)), errno);
199  }
200 #endif
201  struct sockaddr_in addr;
202  bzero(&addr, sizeof(struct sockaddr_in));
203  addr.sin_family = AF_INET;
204  std::memcpy(&addr.sin_addr, remote->h_addr_list[0], remote->h_length);
205  addr.sin_port = htons(port);
206  if (::connect(m_fd, (struct sockaddr *)&addr, sizeof(addr))) {
207  throw tasks_exception(tasks_error::SOCKET_CONNECT, "connect failed: " + std::string(std::strerror(errno)),
208  errno);
209  }
210  if (!m_blocking) {
211  if (fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL, 0) | O_NONBLOCK)) {
212  throw tasks_exception(tasks_error::SOCKET_FNCTL, "fcntl failed: " + std::string(std::strerror(errno)),
213  errno);
214  }
215  }
216 }
217 
219  if (m_fd > -1) {
220  ::shutdown(m_fd, SHUT_RDWR);
221  m_fd = -1;
222  }
223 }
224 
225 std::streamsize socket::write(const char *data, std::size_t len, int port, std::string ip) {
226  if (m_fd == -1 && udp()) {
227  m_fd = ::socket(AF_INET, SOCK_DGRAM, 0);
228  if (m_fd < 0) {
229  throw tasks_exception(tasks_error::SOCKET_SOCKET, "socket failed: " + std::string(std::strerror(errno)),
230  errno);
231  }
232  }
233  if (port > -1) {
234  init_sockaddr(port, ip);
235  }
236  const sockaddr *addr = nullptr;
237  socklen_t addr_len = 0;
238  if (nullptr != m_addr) {
239  addr = (const sockaddr *)m_addr.get();
240  addr_len = sizeof(*addr);
241  }
242  ssize_t bytes = sendto(m_fd, data, len, SEND_RECV_FLAGS, addr, addr_len);
243  if (bytes < 0 && errno != EAGAIN) {
244  std::stringstream s;
245  s << "error writing to client file descriptor " << m_fd << ": " << std::strerror(errno);
246  throw tasks_exception(tasks_error::SOCKET_WRITE, s.str(), errno);
247  }
248  return bytes;
249 }
250 
251 std::streamsize socket::read(char *data, std::size_t len) {
252  sockaddr *addr = nullptr;
253  socklen_t addr_len = 0;
254  if (udp() && nullptr != m_addr) {
255  addr = (sockaddr *)m_addr.get();
256  addr_len = sizeof(*addr);
257  }
258  ssize_t bytes = recvfrom(m_fd, data, len, SEND_RECV_FLAGS, addr, &addr_len);
259  if (bytes < 0 && errno != EAGAIN) {
260  std::stringstream s;
261  s << "error reading from client file descriptor " << m_fd << ": " << std::strerror(errno);
262  throw tasks_exception(tasks_error::SOCKET_READ, s.str(), errno);
263  } else if (bytes == 0) {
264  std::stringstream s;
265  s << "client " << m_fd << " disconnected";
267  }
268  return bytes;
269 }
270 
271 } // net
272 } // tasks
void bind(int port, std::string ip="")
Definition: socket.cpp:86
int m_fd
Definition: io_base.h:32
socket_type
Definition: socket.h:32
Error on sendto sys call.
std::streamsize write(const char *data, std::size_t len, int port=-1, std::string ip="")
Definition: socket.cpp:225
Error on chmod sys call (domain sockets)
Error on setsockopt sys call.
#define terr(m)
Definition: logging.h:57
#define SEND_RECV_FLAGS
Definition: socket.h:24
The socket class.
Definition: socket.h:35
std::shared_ptr< struct sockaddr_in > addr()
Definition: socket.h:58
Error on bind sys call.
std::streamsize read(char *data, std::size_t len)
Definition: socket.cpp:251
Error on fnctl sys call.
Tasks execption class.
Base class for socket and term.
Definition: io_base.h:16
socket accept()
Accept new client connections.
Definition: socket.cpp:136
Error when calling listen on a UDP socket.
void shutdown()
Call shutdown on the fd.
Definition: socket.cpp:218
Error on setsockopt sys call.
Error on listen sys call.
Error on connect sys call.
Error when reading from a disconnected socket.
Error on accept sys call.
void init_sockaddr(int port, std::string ip="")
Definition: socket.cpp:119
Error on recvfrom sys call.
socket_type m_type
Definition: socket.h:116
void connect(std::string path)
Definition: socket.cpp:147
Error on socket sys call.
socket(int fd)
Definition: socket.h:40
bool udp() const
Definition: socket.h:49
void listen(std::string path, int queue_size=128)
Definition: socket.cpp:36
std::shared_ptr< struct sockaddr_in > m_addr
Definition: socket.h:118
Error when trying to connect to a host that can't be resolved.