Migrate libasyncio from boost.signal to signals2 (#8756)
[libasyncio] / asyncio / async_socket.cpp
CommitLineData
8c15b8c7
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
5c8a3d40
RP
20/** @file
21 *
6ac1fb46
RP
22 * @copyright Copyright 2008-2009 by Intra2net AG
23 * @contact Intra2net Opensource Team \<opensource@intra2net.com\>
5c8a3d40
RP
24 *
25 * @todo unlink unix server socket on close.
26 */
27
42b7c46d 28#include "async_socket.hpp"
5c8a3d40
RP
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <sys/socket.h>
32#include <errno.h>
33#include <sys/un.h>
34#include <netinet/in.h>
35#include <limits.h>
fd6ea89e 36#include <asyncio_system_tools.cpp>
5c8a3d40
RP
37
38
42b7c46d 39namespace AsyncIo
5c8a3d40
RP
40{
41
42
43namespace
44{
45
e4e9cd8c
TJ
46/**
47 Struct for holding sockaddr.
48
49 It's called "MegaAddr" because we use a trick to extend the historical UNIX domain socket
50 path length of 108 bytes to PATH_MAX by declaring a large enough buffer
51 at the end of the union.
52
53 This works as long as the POSIX functions don't verify the buffer length.
54 For glibc on linux this is true.
a8405184
TJ
55
56 UPDATE 2011-08-26:
57 Removed that buffer as the gcc stack protector chokes on this.
58
59 Now we are "limited" to 108 bytes again. The longest socket path
60 currently in use on the Intranator is 68 bytes. It looks already
61 longer than Richard Stallman's beard, so it should be fine.
e4e9cd8c 62*/
5c8a3d40
RP
63union MegaAddr {
64 struct sockaddr m_addr;
5c8a3d40 65 struct sockaddr_un m_addr_un; // NOTE (historically) too small...
5c8a3d40
RP
66};
67
68
69} // eo namespace <anonymous>
70
71
72
73/*
74** implementation of ServerSocketBaseImplementation
75*/
76
77
78ServerSocketBaseImplementation::ServerSocketBaseImplementation()
79: IOImplementation()
80{
81} // eo ServerSocketBaseImplementation::ServerSocketBaseImplementation()
82
83
84/**
85 * @brief handled incoming connections on the server port.
86 *
87 * accepts the new connection, stores the peer address in an internal buffer
6ac1fb46 88 * and calls a (derived) acceptNewConnection method to create an appropriate
5c8a3d40
RP
89 * IO class instance.
90 * If no io instance is created the connection is closed.
91 */
92void ServerSocketBaseImplementation::doRead()
93{
94 MegaAddr addr;
95 socklen_t addrlen = sizeof(addr);
96
97 // reset errno:
98 m_errno= 0;
99
100 // reset the mark:
101 resetReadMark();
102
103 int fd = ::accept( readFd(), &addr.m_addr, &addrlen);
104 if (fd < 0)
105 {
106 // handle errors.
107 m_errno= errno;
108 switch (m_errno)
109 {
110 case EBADF:
111 case EINVAL:
112 case ENOTSOCK:
113 // should not happen...
114 close();
115 break;
116 default:;
117 }
118 return;
119 }
120
a8405184 121 if (addrlen >= sizeof(addr))
5c8a3d40
RP
122 {
123 //something went terribly wrong!!
124 // the resulting address structure is larger than it ever could be...
125 ::close(fd);
126 return;
127 }
128
129 IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
130 if(!connection)
131 {
132 ::close(fd);
133 return;
134 }
135 if (m_new_connection_base_callback)
136 {
137 m_new_connection_base_callback(connection);
138 }
139} // eo ServerSocketBaseImplementation::doRead()
140
141
142/**
143 * @brief handles write events.
144 *
145 * since server sockets never ever get a write mark, something must
146 * be wrong and the connection is closed!
147 */
148void ServerSocketBaseImplementation::doWrite()
149{
150 // if this method is called, something went wrong...
151 close();
152 //TODO throw something?!
153} // eo ServerSocketBaseImplementation::doWrite()
154
155
156
157/**
158 * @brief sets a function which is called when a new connection was established.
159 *
6ac1fb46 160 * The function gets a (shared) pointer to the new connection as parameter and is
5c8a3d40 161 * expected to store it when it accepts the connection.
6ac1fb46 162 * (Else the connection gets deleted after the function was called.)
5c8a3d40 163 *
6ac1fb46 164 * @param func the function which should be called on new connections.
5c8a3d40
RP
165 */
166void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
167 const NewConnectionBaseCallbackFunc& func)
168{
169 m_new_connection_base_callback= func;
170} // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
171
172
173
174/**
175 * @brief callback for new connections.
176 *
177 * The base method always returns an empty pointer.
178 *
179 * Derived classes must override this method and do something useful with
180 * the passed file descriptor.
181 *
182 * @param fd the file descriptor of the new connection.
183 * @param addr pointer to the address structure filled from ::accept()
184 * @return shared pointer to the new IO class instance (; empty if not accepted)
185 */
186IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
187 int fd, boost::any addr)
188{
189 // needs to be defined in derived class!
190 return IOImplementationPtr();
191} // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
192
193
194
195/*
196** implementation of UnixIOSocket
197*/
198
199
200UnixIOSocket::UnixIOSocket()
6ac1fb46
RP
201: m_peer_pid((unsigned int)-1)
202, m_peer_uid((unsigned int)-1)
203, m_peer_gid((unsigned int)-1)
5c8a3d40
RP
204{
205} // eo UnixIOSocket::UnixIOSocket()
206
207
208
209UnixIOSocket::UnixIOSocket(const std::string& path)
6ac1fb46
RP
210: m_peer_pid((unsigned int)-1)
211, m_peer_uid((unsigned int)-1)
212, m_peer_gid((unsigned int)-1)
5c8a3d40 213{
6ac1fb46 214 open(path);
5c8a3d40
RP
215} // eo UnixIOSocket::UnixIOSocket(const std::string&)
216
217
6ac1fb46 218UnixIOSocket::UnixIOSocket(int fd,const std::string& path)
5c8a3d40
RP
219: IOImplementation(fd,fd)
220, m_path(path)
6ac1fb46
RP
221, m_peer_pid((unsigned int)-1)
222, m_peer_uid((unsigned int)-1)
223, m_peer_gid((unsigned int)-1)
5c8a3d40 224{
6ac1fb46
RP
225 update_peer_information(fd);
226} // eo UnixIOSocket::UnixIOSocket(int,const std::string&)
5c8a3d40
RP
227
228
229/**
230 * @brief opens a (client) connection to an unix domain socket.
231 *
232 * @param path the path the server is listening on.
233 * @return @a true iff the connection was successfully opened.
234 */
235bool UnixIOSocket::open(const std::string& path)
236{
237 if (opened()) close();
238
239 m_errno= 0;
240 m_path.clear();
241
242 if (path.empty() || path.size() >= PATH_MAX)
243 {
244 return false;
245 }
246
247 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
248 if (fd<0)
249 {
250 m_errno= errno;
251 return false;
252 }
253
254 {
255 MegaAddr addr;
256 addr.m_addr_un.sun_family= AF_UNIX;
a8405184
TJ
257
258 strncpy(addr.m_addr_un.sun_path, path.c_str(), sizeof(addr.m_addr_un.sun_path));
259 addr.m_addr_un.sun_path[sizeof(addr.m_addr_un.sun_path)-1] = '\0';
260
1262d853 261 if (::connect(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0) //lint !e413
5c8a3d40
RP
262 {
263 m_errno= errno;
264 ::close(fd);
265 return false;
266 }
267 }
268 m_path= path;
269 setReadFd(fd);
270 setWriteFd(fd);
271 return true;
272} // eo UnixIOSocket::open(const std::string&,int)
273
274
6ac1fb46
RP
275/**
276 * @brief update the peer info (pid,gid,uid).
277 * @param fd the socket to update the information for.
278 * @return @a true if succesfully determined the peer info.
279 *
280 * @note if the peer info could not be detected the values are set to
281 * (unsigned int)(-1).
282 */
283bool UnixIOSocket::update_peer_information(int fd)
284{
285 m_peer_pid=(unsigned int)-1;
286 m_peer_gid=(unsigned int)-1;
287 m_peer_uid=(unsigned int)-1;
288
289 //TODO add more versions of getting the peer information here...
290 // BSD systems seems to have SO_PEERCRED,too (does someone know how it is used there?)
291
292 #ifdef __linux__
293 { // the linux way to get peer info (pid,gid,uid):
294 struct ucred cred;
295 socklen_t cred_len = sizeof(cred);
296 if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
297 {
298 m_peer_pid= cred.pid;
299 m_peer_uid= cred.uid;
300 m_peer_gid= cred.gid;
301 return true;
302 }
303 }
304 #else
305 #warning dont know how to determine peer info.
306 #endif
307 return false;
308} // end of UnixIOSocketupdate_peer_information(int)
309
310
5c8a3d40
RP
311/*
312** implementation of UnixServerSocketBase
313*/
314
315
316UnixServerSocketBase::UnixServerSocketBase()
317{
318} // eo UnixServerSocketBase::UnixServerSocketBase
319
320
321UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
322{
323 open(path,mode);
324} // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
325
326
327/**
328 * @brief opens the server part of an unix domain socket.
329 *
330 * @param path the path the new port should listen on.
331 * @param mode the mode for the path.
332 * @return @a true iff the port was successfully opened.
333 */
334bool UnixServerSocketBase::open(const std::string& path, int mode)
335{
336 if (opened()) close();
337 m_errno= 0;
338 if (path.empty() || path.size() >= PATH_MAX)
339 {
340 return false;
341 }
342
343 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
344 if (fd<0)
345 {
346 m_errno= errno;
347 return false;
348 }
349
350 {
351 MegaAddr addr;
352 addr.m_addr_un.sun_family= AF_UNIX;
a8405184
TJ
353
354 strncpy(addr.m_addr_un.sun_path, path.c_str(), sizeof(addr.m_addr_un.sun_path));
355 addr.m_addr_un.sun_path[sizeof(addr.m_addr_un.sun_path)-1] = '\0';
356
fd6ea89e 357 Utils::unlink(path); // just in case...
6ac1fb46
RP
358 // NOTE this is a place which might require some updates for multithreaded
359 // usage! (setting the umask affects all threads...)
5c8a3d40 360 mode_t old_mask= ::umask( (mode & 0777) ^ 0777);
1262d853 361 if (::bind(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0) //lint !e413
5c8a3d40
RP
362 {
363 m_errno= errno;
364 ::umask(old_mask);
365 ::close(fd);
366 return false;
367 }
368 ::umask(old_mask);
369 }
370 m_path= path;
371
372 {
373 int res= ::listen(fd,8);
374 if (res < 0)
375 {
376 m_errno= errno;
377 ::close(fd);
378 return false;
379 }
380 }
381 setReadFd(fd);
382 return true;
383} // eo UnixServerSocketBase::open(const std::string&,int)
384
385
386/**
387 * @brief called from base class to create a new connection instance.
388 *
389 * This method accepts only connections to unix domain sockets.
390 * It also tries to determine the peer pid, uid and gid.
391 *
392 * @param fd the file descriptor of a freshly accepted connection.
6ac1fb46
RP
393 * @param addr contains "pointer to struct sockaddr"
394 * @return @a a (shared) pointer to the new connection instance; empty if none was
5c8a3d40
RP
395 * created.
396 */
397IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
398{
399 struct sockaddr *addr_ptr= NULL;
400 try {
401 addr_ptr = boost::any_cast<struct sockaddr*>(addr);
402 }
403 catch (boost::bad_any_cast&)
404 {
405 return IOImplementationPtr();
406 }
407 // check for the right family:
408 if (addr_ptr->sa_family != AF_UNIX)
409 {
410 return IOImplementationPtr();
411 }
412 struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
413 std::string peer_path( un_ptr->sun_path );
5c8a3d40 414
6ac1fb46
RP
415 UnixIOSocketPtr ptr( createIOSocket(fd, peer_path) );
416
5c8a3d40
RP
417 return ptr;
418} // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
419
420
421/**
422 * @brief "real" creator of the connection instance.
423 *
424 * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
425 *
426 * @param fd file descriptor for the socket
427 * @param path path as delivered by peer.
5c8a3d40
RP
428 * @return (shared) pointer to the new io instance.
429 */
430UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
6ac1fb46
RP
431 int fd, const std::string& path
432)
5c8a3d40 433{
6ac1fb46
RP
434 return UnixIOSocketPtr
435 (
436 new UnixIOSocket(fd, path)
5c8a3d40 437 );
6ac1fb46 438} // eo UnixServerSocketBase::createIOSocket(int,const std::string&)
5c8a3d40
RP
439
440
441
42b7c46d 442}// eo namespace AsyncIo