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>
47 Struct for holding sockaddr.
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.
53 This works as long as the POSIX functions don't verify the buffer length.
54 For glibc on linux this is true.
57 Removed that buffer as the gcc stack protector chokes on this.
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.
64 struct sockaddr m_addr;
65 struct sockaddr_un m_addr_un; // NOTE (historically) too small...
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 appropriate
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 //something went terribly wrong!!
124 // the resulting address structure is larger than it ever could be...
129 IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
135 if (m_new_connection_base_callback)
137 m_new_connection_base_callback(connection);
139 } // eo ServerSocketBaseImplementation::doRead()
143 * @brief handles write events.
145 * since server sockets never ever get a write mark, something must
146 * be wrong and the connection is closed!
148 void ServerSocketBaseImplementation::doWrite()
150 // if this method is called, something went wrong...
152 //TODO throw something?!
153 } // eo ServerSocketBaseImplementation::doWrite()
158 * @brief sets a function which is called when a new connection was established.
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.)
164 * @param func the function which should be called on new connections.
166 void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
167 const NewConnectionBaseCallbackFunc& func)
169 m_new_connection_base_callback= func;
170 } // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
175 * @brief callback for new connections.
177 * The base method always returns an empty pointer.
179 * Derived classes must override this method and do something useful with
180 * the passed file descriptor.
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)
186 IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
187 int fd, boost::any addr)
189 // needs to be defined in derived class!
190 return IOImplementationPtr();
191 } // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
196 ** implementation of UnixIOSocket
200 UnixIOSocket::UnixIOSocket()
201 : m_peer_pid((unsigned int)-1)
202 , m_peer_uid((unsigned int)-1)
203 , m_peer_gid((unsigned int)-1)
205 } // eo UnixIOSocket::UnixIOSocket()
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)
215 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
218 UnixIOSocket::UnixIOSocket(int fd,const std::string& path)
219 : IOImplementation(fd,fd)
221 , m_peer_pid((unsigned int)-1)
222 , m_peer_uid((unsigned int)-1)
223 , m_peer_gid((unsigned int)-1)
225 update_peer_information(fd);
226 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&)
230 * @brief opens a (client) connection to an unix domain socket.
232 * @param path the path the server is listening on.
233 * @return @a true iff the connection was successfully opened.
235 bool UnixIOSocket::open(const std::string& path)
237 if (opened()) close();
242 if (path.empty() || path.size() >= PATH_MAX)
247 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
256 addr.m_addr_un.sun_family= AF_UNIX;
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';
261 if (::connect(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0) //lint !e413
272 } // eo UnixIOSocket::open(const std::string&,int)
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.
280 * @note if the peer info could not be detected the values are set to
281 * (unsigned int)(-1).
283 bool UnixIOSocket::update_peer_information(int fd)
285 m_peer_pid=(unsigned int)-1;
286 m_peer_gid=(unsigned int)-1;
287 m_peer_uid=(unsigned int)-1;
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?)
293 { // the linux way to get peer info (pid,gid,uid):
295 socklen_t cred_len = sizeof(cred);
296 if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
298 m_peer_pid= cred.pid;
299 m_peer_uid= cred.uid;
300 m_peer_gid= cred.gid;
305 #warning dont know how to determine peer info.
308 } // end of UnixIOSocketupdate_peer_information(int)
312 ** implementation of UnixServerSocketBase
316 UnixServerSocketBase::UnixServerSocketBase()
318 } // eo UnixServerSocketBase::UnixServerSocketBase
321 UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
324 } // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
328 * @brief opens the server part of an unix domain socket.
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.
334 bool UnixServerSocketBase::open(const std::string& path, int mode)
336 if (opened()) close();
338 if (path.empty() || path.size() >= PATH_MAX)
343 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
352 addr.m_addr_un.sun_family= AF_UNIX;
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';
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
373 int res= ::listen(fd,8);
383 } // eo UnixServerSocketBase::open(const std::string&,int)
387 * @brief called from base class to create a new connection instance.
389 * This method accepts only connections to unix domain sockets.
390 * It also tries to determine the peer pid, uid and gid.
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
397 IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
399 struct sockaddr *addr_ptr= NULL;
401 addr_ptr = boost::any_cast<struct sockaddr*>(addr);
403 catch (boost::bad_any_cast&)
405 return IOImplementationPtr();
407 // check for the right family:
408 if (addr_ptr->sa_family != AF_UNIX)
410 return IOImplementationPtr();
412 struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
413 std::string peer_path( un_ptr->sun_path );
415 UnixIOSocketPtr ptr( createIOSocket(fd, peer_path) );
418 } // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
422 * @brief "real" creator of the connection instance.
424 * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
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.
430 UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
431 int fd, const std::string& path
434 return UnixIOSocketPtr
436 new UnixIOSocket(fd, path)
438 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&)
442 }// eo namespace AsyncIo