2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
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.
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.
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.
22 * @copyright Copyright 2008-2009 by Intra2net AG
23 * @contact Intra2net Opensource Team \<opensource@intra2net.com\>
25 * @todo unlink unix server socket on close.
28 #include "async_socket.hpp"
29 #include <sys/types.h>
31 #include <sys/socket.h>
34 #include <netinet/in.h>
36 #include <asyncio_system_tools.cpp>
46 struct sockaddr_un dummy_un;
49 Struct for holding sockaddr.
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.
55 This works as long as the POSIX functions don't verify the buffer length.
56 For glibc on linux this is true.
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 ];
70 } // eo namespace <anonymous>
75 ** implementation of ServerSocketBaseImplementation
79 ServerSocketBaseImplementation::ServerSocketBaseImplementation()
82 } // eo ServerSocketBaseImplementation::ServerSocketBaseImplementation()
86 * @brief handled incoming connections on the server port.
88 * accepts the new connection, stores the peer address in an internal buffer
89 * and calls a (derived) acceptNewConnection method to create an appropriate
91 * If no io instance is created the connection is closed.
93 void ServerSocketBaseImplementation::doRead()
96 socklen_t addrlen = sizeof(addr);
104 int fd = ::accept( readFd(), &addr.m_addr, &addrlen);
114 // should not happen...
122 if (addrlen < sizeof(addr))
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;
130 //something went terribly wrong!!
131 // the resulting address structure is larger than it ever could be...
136 IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
142 if (m_new_connection_base_callback)
144 m_new_connection_base_callback(connection);
146 } // eo ServerSocketBaseImplementation::doRead()
150 * @brief handles write events.
152 * since server sockets never ever get a write mark, something must
153 * be wrong and the connection is closed!
155 void ServerSocketBaseImplementation::doWrite()
157 // if this method is called, something went wrong...
159 //TODO throw something?!
160 } // eo ServerSocketBaseImplementation::doWrite()
165 * @brief sets a function which is called when a new connection was established.
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.)
171 * @param func the function which should be called on new connections.
173 void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
174 const NewConnectionBaseCallbackFunc& func)
176 m_new_connection_base_callback= func;
177 } // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
182 * @brief callback for new connections.
184 * The base method always returns an empty pointer.
186 * Derived classes must override this method and do something useful with
187 * the passed file descriptor.
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)
193 IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
194 int fd, boost::any addr)
196 // needs to be defined in derived class!
197 return IOImplementationPtr();
198 } // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
203 ** implementation of UnixIOSocket
207 UnixIOSocket::UnixIOSocket()
208 : m_peer_pid((unsigned int)-1)
209 , m_peer_uid((unsigned int)-1)
210 , m_peer_gid((unsigned int)-1)
212 } // eo UnixIOSocket::UnixIOSocket()
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)
222 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
225 UnixIOSocket::UnixIOSocket(int fd,const std::string& path)
226 : IOImplementation(fd,fd)
228 , m_peer_pid((unsigned int)-1)
229 , m_peer_uid((unsigned int)-1)
230 , m_peer_gid((unsigned int)-1)
232 update_peer_information(fd);
233 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&)
237 * @brief opens a (client) connection to an unix domain socket.
239 * @param path the path the server is listening on.
240 * @return @a true iff the connection was successfully opened.
242 bool UnixIOSocket::open(const std::string& path)
244 if (opened()) close();
249 if (path.empty() || path.size() >= PATH_MAX)
254 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
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
276 } // eo UnixIOSocket::open(const std::string&,int)
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.
284 * @note if the peer info could not be detected the values are set to
285 * (unsigned int)(-1).
287 bool UnixIOSocket::update_peer_information(int fd)
289 m_peer_pid=(unsigned int)-1;
290 m_peer_gid=(unsigned int)-1;
291 m_peer_uid=(unsigned int)-1;
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?)
297 { // the linux way to get peer info (pid,gid,uid):
299 socklen_t cred_len = sizeof(cred);
300 if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
302 m_peer_pid= cred.pid;
303 m_peer_uid= cred.uid;
304 m_peer_gid= cred.gid;
309 #warning dont know how to determine peer info.
312 } // end of UnixIOSocketupdate_peer_information(int)
316 ** implementation of UnixServerSocketBase
320 UnixServerSocketBase::UnixServerSocketBase()
322 } // eo UnixServerSocketBase::UnixServerSocketBase
325 UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
328 } // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
332 * @brief opens the server part of an unix domain socket.
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.
338 bool UnixServerSocketBase::open(const std::string& path, int mode)
340 if (opened()) close();
342 if (path.empty() || path.size() >= PATH_MAX)
347 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
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
374 int res= ::listen(fd,8);
384 } // eo UnixServerSocketBase::open(const std::string&,int)
388 * @brief called from base class to create a new connection instance.
390 * This method accepts only connections to unix domain sockets.
391 * It also tries to determine the peer pid, uid and gid.
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
398 IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
400 struct sockaddr *addr_ptr= NULL;
402 addr_ptr = boost::any_cast<struct sockaddr*>(addr);
404 catch (boost::bad_any_cast&)
406 return IOImplementationPtr();
408 // check for the right family:
409 if (addr_ptr->sa_family != AF_UNIX)
411 return IOImplementationPtr();
413 struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
414 std::string peer_path( un_ptr->sun_path );
416 UnixIOSocketPtr ptr( createIOSocket(fd, peer_path) );
419 } // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
423 * @brief "real" creator of the connection instance.
425 * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
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.
431 UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
432 int fd, const std::string& path
435 return UnixIOSocketPtr
437 new UnixIOSocket(fd, path)
439 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&)
443 }// eo namespace AsyncIo