PC-lint: Silence warning about buffer possible overflow - we do this by design :)
[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 struct sockaddr_un dummy_un;
47
48 /**
49     Struct for holding sockaddr.
50
51     It's called "MegaAddr" because we use a trick to extend the historical UNIX domain socket
52     path length of 108 bytes to PATH_MAX by declaring a large enough buffer
53     at the end of the union.
54
55     This works as long as the POSIX functions don't verify the buffer length.
56     For glibc on linux this is true.
57 */
58 union MegaAddr {
59    struct sockaddr         m_addr;
60    struct sockaddr_in      m_addr_in;
61    struct sockaddr_un      m_addr_un; // NOTE (historically) too small...
62    // storage is large enough to hold all sockaddr_* variants with the (historical) exception of _un !
63    struct sockaddr_storage m_addr_store;
64    // a char array large enough to hold _un (with an path up to the maximum allowed size!)
65    // (the +1 is added for a later 0-termination of the path)
66    char m_buffer[ sizeof(dummy_un) - sizeof(dummy_un.sun_path) + PATH_MAX + 1 ];
67 };
68
69
70 } // eo namespace <anonymous>
71
72
73
74 /*
75 ** implementation of ServerSocketBaseImplementation
76 */
77
78
79 ServerSocketBaseImplementation::ServerSocketBaseImplementation()
80 : IOImplementation()
81 {
82 } // eo ServerSocketBaseImplementation::ServerSocketBaseImplementation()
83
84
85 /**
86  * @brief handled incoming connections on the server port.
87  *
88  * accepts the new connection, stores the peer address in an internal buffer
89  * and calls a (derived) acceptNewConnection method to create an appropriate
90  * IO class instance.
91  * If no io instance is created the connection is closed.
92  */
93 void ServerSocketBaseImplementation::doRead()
94 {
95    MegaAddr addr;
96    socklen_t addrlen = sizeof(addr);
97
98    // reset errno:
99    m_errno= 0;
100
101    // reset the mark:
102    resetReadMark();
103
104    int fd = ::accept( readFd(), &addr.m_addr, &addrlen);
105    if (fd < 0)
106    {
107       // handle errors.
108       m_errno= errno;
109       switch (m_errno)
110       {
111          case EBADF:
112          case EINVAL:
113          case ENOTSOCK:
114             // should not happen...
115             close();
116             break;
117          default:;
118       }
119       return;
120    }
121
122    if (addrlen < sizeof(addr))
123    {
124       // in case of unix domain socket: terminate the path!
125       // NOTE we are doing this here since we don't pass the length info.
126       addr.m_buffer[addrlen]= 0;
127    }
128    else
129    {
130       //something went terribly wrong!!
131       // the resulting address structure is larger than it ever could be...
132       ::close(fd);
133       return;
134    }
135
136    IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
137    if(!connection)
138    {
139       ::close(fd);
140       return;
141    }
142    if (m_new_connection_base_callback)
143    {
144       m_new_connection_base_callback(connection);
145    }
146 } // eo ServerSocketBaseImplementation::doRead()
147
148
149 /**
150  * @brief handles write events.
151  *
152  * since server sockets never ever get a write mark, something must
153  * be wrong and the connection is closed!
154  */
155 void ServerSocketBaseImplementation::doWrite()
156 {
157    // if this method is called, something went wrong...
158    close();
159    //TODO throw something?!
160 } // eo ServerSocketBaseImplementation::doWrite()
161
162
163
164 /**
165  * @brief sets a function which is called when a new connection was established.
166  *
167  * The function gets a (shared) pointer to the new connection as parameter and is
168  * expected to store it when it accepts the connection.
169  * (Else the connection gets deleted after the function was called.)
170  *
171  * @param func the function which should be called on new connections.
172  */
173 void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
174    const NewConnectionBaseCallbackFunc& func)
175 {
176    m_new_connection_base_callback= func;
177 } // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
178
179
180
181 /**
182  * @brief callback for new connections.
183  *
184  * The base method always returns an empty pointer.
185  *
186  * Derived classes must override this method and do something useful with
187  * the passed file descriptor.
188  *
189  * @param fd the file descriptor of the new connection.
190  * @param addr pointer to the address structure filled from ::accept()
191  * @return shared pointer to the new IO class instance (; empty if not accepted)
192  */
193 IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
194    int fd, boost::any addr)
195 {
196    // needs to be defined in derived class!
197    return IOImplementationPtr();
198 } // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
199
200
201
202 /*
203 ** implementation of UnixIOSocket
204 */
205
206
207 UnixIOSocket::UnixIOSocket()
208 : m_peer_pid((unsigned int)-1)
209 , m_peer_uid((unsigned int)-1)
210 , m_peer_gid((unsigned int)-1)
211 {
212 } // eo UnixIOSocket::UnixIOSocket()
213
214
215
216 UnixIOSocket::UnixIOSocket(const std::string& path)
217 : m_peer_pid((unsigned int)-1)
218 , m_peer_uid((unsigned int)-1)
219 , m_peer_gid((unsigned int)-1)
220 {
221     open(path);
222 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
223
224
225 UnixIOSocket::UnixIOSocket(int fd,const std::string& path)
226 : IOImplementation(fd,fd)
227 , m_path(path)
228 , m_peer_pid((unsigned int)-1)
229 , m_peer_uid((unsigned int)-1)
230 , m_peer_gid((unsigned int)-1)
231 {
232     update_peer_information(fd);
233 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&)
234
235
236 /**
237  * @brief opens a (client) connection to an unix domain socket.
238  *
239  * @param path the path the server is listening on.
240  * @return @a true iff the connection was successfully opened.
241  */
242 bool UnixIOSocket::open(const std::string& path)
243 {
244    if (opened()) close();
245
246    m_errno= 0;
247    m_path.clear();
248
249    if (path.empty() || path.size() >= PATH_MAX)
250    {
251       return false;
252    }
253
254    int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
255    if (fd<0)
256    {
257       m_errno= errno;
258       return false;
259    }
260
261    {
262       MegaAddr addr;
263       addr.m_addr_un.sun_family= AF_UNIX;
264       strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX);                             //lint !e419
265       if (::connect(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)           //lint !e413
266       {
267          m_errno= errno;
268          ::close(fd);
269          return false;
270       }
271    }
272    m_path= path;
273    setReadFd(fd);
274    setWriteFd(fd);
275    return true;
276 } // eo UnixIOSocket::open(const std::string&,int)
277
278
279 /**
280  * @brief update the peer info (pid,gid,uid).
281  * @param fd the socket to update the information for.
282  * @return @a true if succesfully determined the peer info.
283  *
284  * @note if the peer info could not be detected the values are set to
285  * (unsigned int)(-1).
286  */
287 bool UnixIOSocket::update_peer_information(int fd)
288 {
289     m_peer_pid=(unsigned int)-1;
290     m_peer_gid=(unsigned int)-1;
291     m_peer_uid=(unsigned int)-1;
292
293     //TODO add more versions of getting the peer information here...
294     // BSD systems seems to have SO_PEERCRED,too (does someone know how it is used there?)
295
296  #ifdef __linux__
297     { // the linux way to get peer info (pid,gid,uid):
298        struct ucred cred;
299        socklen_t cred_len = sizeof(cred);
300        if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
301        {
302           m_peer_pid= cred.pid;
303           m_peer_uid= cred.uid;
304           m_peer_gid= cred.gid;
305           return true;
306        }
307     }
308  #else
309  #warning dont know how to determine peer info.
310  #endif
311     return false;
312 } // end of UnixIOSocketupdate_peer_information(int)
313
314
315 /*
316 ** implementation of UnixServerSocketBase
317 */
318
319
320 UnixServerSocketBase::UnixServerSocketBase()
321 {
322 } // eo UnixServerSocketBase::UnixServerSocketBase
323
324
325 UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
326 {
327    open(path,mode);
328 } // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
329
330
331 /**
332  * @brief opens the server part of an unix domain socket.
333  *
334  * @param path the path the new port should listen on.
335  * @param mode the mode for the path.
336  * @return @a true iff the port was successfully opened.
337  */
338 bool UnixServerSocketBase::open(const std::string& path, int mode)
339 {
340    if (opened()) close();
341    m_errno= 0;
342    if (path.empty() || path.size() >= PATH_MAX)
343    {
344       return false;
345    }
346
347    int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
348    if (fd<0)
349    {
350       m_errno= errno;
351       return false;
352    }
353
354    {
355       MegaAddr addr;
356       addr.m_addr_un.sun_family= AF_UNIX;
357       strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX);                             //lint !e419
358       Utils::unlink(path); // just in case...
359       // NOTE this is a place which might require some updates for multithreaded
360       // usage! (setting the umask affects all threads...)
361       mode_t old_mask= ::umask( (mode & 0777) ^ 0777);
362       if (::bind(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)              //lint !e413
363       {
364          m_errno= errno;
365          ::umask(old_mask);
366          ::close(fd);
367          return false;
368       }
369       ::umask(old_mask);
370    }
371    m_path= path;
372
373    {
374       int res= ::listen(fd,8);
375       if (res < 0)
376       {
377          m_errno= errno;
378          ::close(fd);
379          return false;
380       }
381    }
382    setReadFd(fd);
383    return true;
384 } // eo UnixServerSocketBase::open(const std::string&,int)
385
386
387 /**
388  * @brief called from base class to create a new connection instance.
389  *
390  * This method accepts only connections to unix domain sockets.
391  * It also tries to determine the peer pid, uid and gid.
392  *
393  * @param fd the file descriptor of a freshly accepted connection.
394  * @param addr contains "pointer to struct sockaddr"
395  * @return @a a (shared) pointer to the new connection instance; empty if none was
396  * created.
397  */
398 IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
399 {
400    struct sockaddr *addr_ptr= NULL;
401    try {
402       addr_ptr = boost::any_cast<struct sockaddr*>(addr);
403    }
404    catch (boost::bad_any_cast&)
405    {
406       return IOImplementationPtr();
407    }
408    // check for the right family:
409    if (addr_ptr->sa_family != AF_UNIX)
410    {
411       return IOImplementationPtr();
412    }
413    struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
414    std::string peer_path( un_ptr->sun_path );
415
416    UnixIOSocketPtr ptr( createIOSocket(fd, peer_path) );
417
418    return ptr;
419 } // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
420
421
422 /**
423  * @brief "real" creator of the connection instance.
424  *
425  * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
426  *
427  * @param fd file descriptor for the socket
428  * @param path path as delivered by peer.
429  * @return (shared) pointer to the new io instance.
430  */
431 UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
432    int fd, const std::string& path
433 )
434 {
435    return UnixIOSocketPtr
436    (
437        new UnixIOSocket(fd, path)
438    );
439 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&)
440
441
442
443 }// eo namespace AsyncIo