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 * (c) Copyright 2008 by Intra2net AG
24 * @todo unlink unix server socket on close.
27 #include "async_socket.hpp"
28 #include <sys/types.h>
30 #include <sys/socket.h>
33 #include <netinet/in.h>
35 #include <asyncio_system_tools.cpp>
45 struct sockaddr_un dummy_un;
48 Struct for holding sockaddr.
50 It's called "MegaAddr" because we use a trick to extend the historical UNIX domain socket
51 path length of 108 bytes to PATH_MAX by declaring a large enough buffer
52 at the end of the union.
54 This works as long as the POSIX functions don't verify the buffer length.
55 For glibc on linux this is true.
58 struct sockaddr m_addr;
59 struct sockaddr_in m_addr_in;
60 struct sockaddr_un m_addr_un; // NOTE (historically) too small...
61 // storage is large enough to hold all sockaddr_* variants with the (historical) exception of _un !
62 struct sockaddr_storage m_addr_store;
63 // a char array large enough to hold _un (with an path up to the maximum allowed size!)
64 // (the +1 is added for a later 0-termination of the path)
65 char m_buffer[ sizeof(dummy_un) - sizeof(dummy_un.sun_path) + PATH_MAX + 1 ];
69 } // eo namespace <anonymous>
74 ** implementation of ServerSocketBaseImplementation
78 ServerSocketBaseImplementation::ServerSocketBaseImplementation()
81 } // eo ServerSocketBaseImplementation::ServerSocketBaseImplementation()
85 * @brief handled incoming connections on the server port.
87 * accepts the new connection, stores the peer address in an internal buffer
88 * and calls a (derived) acceptNewConnection method to create an apropriate
90 * If no io instance is created the connection is closed.
92 void ServerSocketBaseImplementation::doRead()
95 socklen_t addrlen = sizeof(addr);
103 int fd = ::accept( readFd(), &addr.m_addr, &addrlen);
113 // should not happen...
121 if (addrlen < sizeof(addr))
123 // in case of unix domain socket: terminate the path!
124 // NOTE we are doing this here since we don't pass the length info.
125 addr.m_buffer[addrlen]= 0;
129 //something went terribly wrong!!
130 // the resulting address structure is larger than it ever could be...
135 IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
141 if (m_new_connection_base_callback)
143 m_new_connection_base_callback(connection);
145 } // eo ServerSocketBaseImplementation::doRead()
149 * @brief handles write events.
151 * since server sockets never ever get a write mark, something must
152 * be wrong and the connection is closed!
154 void ServerSocketBaseImplementation::doWrite()
156 // if this method is called, something went wrong...
158 //TODO throw something?!
159 } // eo ServerSocketBaseImplementation::doWrite()
164 * @brief sets a function which is called when a new connection was established.
166 * The function gets a (shared) pointer to the new connetion as parameter and is
167 * expected to store it when it accepts the connection.
168 * (Else the conenction gets deleted after the function was called.)
170 * @param func the function which hsould be called on new conenctions.
172 void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
173 const NewConnectionBaseCallbackFunc& func)
175 m_new_connection_base_callback= func;
176 } // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
181 * @brief callback for new connections.
183 * The base method always returns an empty pointer.
185 * Derived classes must override this method and do something useful with
186 * the passed file descriptor.
188 * @param fd the file descriptor of the new connection.
189 * @param addr pointer to the address structure filled from ::accept()
190 * @return shared pointer to the new IO class instance (; empty if not accepted)
192 IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
193 int fd, boost::any addr)
195 // needs to be defined in derived class!
196 return IOImplementationPtr();
197 } // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
202 ** implementation of UnixIOSocket
206 UnixIOSocket::UnixIOSocket()
211 } // eo UnixIOSocket::UnixIOSocket()
215 UnixIOSocket::UnixIOSocket(const std::string& path)
221 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
224 UnixIOSocket::UnixIOSocket(
225 int fd, const std::string& path,
226 unsigned int peer_pid, unsigned int peer_uid, unsigned int peer_gid)
227 : IOImplementation(fd,fd)
229 , m_peer_pid(peer_pid)
230 , m_peer_uid(peer_uid)
231 , m_peer_gid(peer_gid)
233 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
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);
265 if (::connect(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)
276 } // eo UnixIOSocket::open(const std::string&,int)
280 ** implementation of UnixServerSocketBase
284 UnixServerSocketBase::UnixServerSocketBase()
286 } // eo UnixServerSocketBase::UnixServerSocketBase
289 UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
292 } // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
296 * @brief opens the server part of an unix domain socket.
298 * @param path the path the new port should listen on.
299 * @param mode the mode for the path.
300 * @return @a true iff the port was successfully opened.
302 bool UnixServerSocketBase::open(const std::string& path, int mode)
304 if (opened()) close();
306 if (path.empty() || path.size() >= PATH_MAX)
311 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
320 addr.m_addr_un.sun_family= AF_UNIX;
321 strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX);
322 Utils::unlink(path); // just in case...
323 mode_t old_mask= ::umask( (mode & 0777) ^ 0777);
324 if (::bind(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)
336 int res= ::listen(fd,8);
346 } // eo UnixServerSocketBase::open(const std::string&,int)
350 * @brief called from base class to create a new connection instance.
352 * This method accepts only connections to unix domain sockets.
353 * It also tries to determine the peer pid, uid and gid.
355 * @param fd the file descriptor of a freshly accepted connection.
356 * @param addr conatins "pointer to struct sockaddr"
357 * @return @a a (shared) pointer to the new connection isntance; empty if none was
360 IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
362 struct sockaddr *addr_ptr= NULL;
364 addr_ptr = boost::any_cast<struct sockaddr*>(addr);
366 catch (boost::bad_any_cast&)
368 return IOImplementationPtr();
370 // check for the right family:
371 if (addr_ptr->sa_family != AF_UNIX)
373 return IOImplementationPtr();
375 struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
376 std::string peer_path( un_ptr->sun_path );
381 { // the linux way to get peer info (pid,gid,uid):
383 socklen_t cred_len = sizeof(cred);
384 if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
392 #error dont know how to determine peer info.
395 UnixIOSocketPtr ptr( createIOSocket(fd, peer_path, peer_pid, peer_uid, peer_gid) );
397 } // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
401 * @brief "real" creator of the connection instance.
403 * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
405 * @param fd file descriptor for the socket
406 * @param path path as delivered by peer.
407 * @param peer_pid peer pid.
408 * @param peer_uid peer uid.
409 * @param peer_gid peer gid.
410 * @return (shared) pointer to the new io instance.
412 UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
413 int fd, const std::string& path,
414 unsigned int peer_pid,
415 unsigned int peer_uid, unsigned int peer_gid)
417 return UnixIOSocketPtr(
418 new UnixIOSocket(fd, path, peer_pid, peer_uid, peer_gid)
420 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
424 }// eo namespace AsyncIo