Migrate libasyncio from boost.signal to signals2 (#8756)
[libasyncio] / asyncio / async_socket.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /** @file
21  *
22  * @copyright Copyright 2008-2009 by Intra2net AG
23  * @contact Intra2net Opensource Team \<opensource@intra2net.com\>
24  *
25  * @todo unlink unix server socket on close.
26  */
27
28 #include "async_socket.hpp"
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>
36 #include <asyncio_system_tools.cpp>
37
38
39 namespace AsyncIo
40 {
41
42
43 namespace
44 {
45
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.
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.
62 */
63 union MegaAddr {
64    struct sockaddr         m_addr;
65    struct sockaddr_un      m_addr_un; // NOTE (historically) too small...
66 };
67
68
69 } // eo namespace <anonymous>
70
71
72
73 /*
74 ** implementation of ServerSocketBaseImplementation
75 */
76
77
78 ServerSocketBaseImplementation::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
88  * and calls a (derived) acceptNewConnection method to create an appropriate
89  * IO class instance.
90  * If no io instance is created the connection is closed.
91  */
92 void 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
121    if (addrlen >= sizeof(addr))
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  */
148 void 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  *
160  * The function gets a (shared) pointer to the new connection as parameter and is
161  * expected to store it when it accepts the connection.
162  * (Else the connection gets deleted after the function was called.)
163  *
164  * @param func the function which should be called on new connections.
165  */
166 void 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  */
186 IOImplementationPtr 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
200 UnixIOSocket::UnixIOSocket()
201 : m_peer_pid((unsigned int)-1)
202 , m_peer_uid((unsigned int)-1)
203 , m_peer_gid((unsigned int)-1)
204 {
205 } // eo UnixIOSocket::UnixIOSocket()
206
207
208
209 UnixIOSocket::UnixIOSocket(const std::string& path)
210 : m_peer_pid((unsigned int)-1)
211 , m_peer_uid((unsigned int)-1)
212 , m_peer_gid((unsigned int)-1)
213 {
214     open(path);
215 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
216
217
218 UnixIOSocket::UnixIOSocket(int fd,const std::string& path)
219 : IOImplementation(fd,fd)
220 , m_path(path)
221 , m_peer_pid((unsigned int)-1)
222 , m_peer_uid((unsigned int)-1)
223 , m_peer_gid((unsigned int)-1)
224 {
225     update_peer_information(fd);
226 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&)
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  */
235 bool 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;
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
261       if (::connect(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)           //lint !e413
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
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  */
283 bool 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
311 /*
312 ** implementation of UnixServerSocketBase
313 */
314
315
316 UnixServerSocketBase::UnixServerSocketBase()
317 {
318 } // eo UnixServerSocketBase::UnixServerSocketBase
319
320
321 UnixServerSocketBase::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  */
334 bool 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;
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
357       Utils::unlink(path); // just in case...
358       // NOTE this is a place which might require some updates for multithreaded
359       // usage! (setting the umask affects all threads...)
360       mode_t old_mask= ::umask( (mode & 0777) ^ 0777);
361       if (::bind(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)              //lint !e413
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.
393  * @param addr contains "pointer to struct sockaddr"
394  * @return @a a (shared) pointer to the new connection instance; empty if none was
395  * created.
396  */
397 IOImplementationPtr 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 );
414
415    UnixIOSocketPtr ptr( createIOSocket(fd, peer_path) );
416
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.
428  * @return (shared) pointer to the new io instance.
429  */
430 UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
431    int fd, const std::string& path
432 )
433 {
434    return UnixIOSocketPtr
435    (
436        new UnixIOSocket(fd, path)
437    );
438 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&)
439
440
441
442 }// eo namespace AsyncIo