3 * (c) Copyright 2008 by Intra2net AG
7 * @todo unlink unix server socket on close.
10 #include "async_socket.hpp"
11 #include <sys/types.h>
13 #include <sys/socket.h>
16 #include <netinet/in.h>
18 #include <asyncio_system_tools.cpp>
28 struct sockaddr_un dummy_un;
31 Struct for holding sockaddr.
33 It's called "MegaAddr" because we use a trick to extend the historical UNIX domain socket
34 path length of 108 bytes to PATH_MAX by declaring a large enough buffer
35 at the end of the union.
37 This works as long as the POSIX functions don't verify the buffer length.
38 For glibc on linux this is true.
41 struct sockaddr m_addr;
42 struct sockaddr_in m_addr_in;
43 struct sockaddr_un m_addr_un; // NOTE (historically) too small...
44 // storage is large enough to hold all sockaddr_* variants with the (historical) exception of _un !
45 struct sockaddr_storage m_addr_store;
46 // a char array large enough to hold _un (with an path up to the maximum allowed size!)
47 // (the +1 is added for a later 0-termination of the path)
48 char m_buffer[ sizeof(dummy_un) - sizeof(dummy_un.sun_path) + PATH_MAX + 1 ];
52 } // eo namespace <anonymous>
57 ** implementation of ServerSocketBaseImplementation
61 ServerSocketBaseImplementation::ServerSocketBaseImplementation()
64 } // eo ServerSocketBaseImplementation::ServerSocketBaseImplementation()
68 * @brief handled incoming connections on the server port.
70 * accepts the new connection, stores the peer address in an internal buffer
71 * and calls a (derived) acceptNewConnection method to create an apropriate
73 * If no io instance is created the connection is closed.
75 void ServerSocketBaseImplementation::doRead()
78 socklen_t addrlen = sizeof(addr);
86 int fd = ::accept( readFd(), &addr.m_addr, &addrlen);
96 // should not happen...
104 if (addrlen < sizeof(addr))
106 // in case of unix domain socket: terminate the path!
107 // NOTE we are doing this here since we don't pass the length info.
108 addr.m_buffer[addrlen]= 0;
112 //something went terribly wrong!!
113 // the resulting address structure is larger than it ever could be...
118 IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
124 if (m_new_connection_base_callback)
126 m_new_connection_base_callback(connection);
128 } // eo ServerSocketBaseImplementation::doRead()
132 * @brief handles write events.
134 * since server sockets never ever get a write mark, something must
135 * be wrong and the connection is closed!
137 void ServerSocketBaseImplementation::doWrite()
139 // if this method is called, something went wrong...
141 //TODO throw something?!
142 } // eo ServerSocketBaseImplementation::doWrite()
147 * @brief sets a function which is called when a new connection was established.
149 * The function gets a (shared) pointer to the new connetion as parameter and is
150 * expected to store it when it accepts the connection.
151 * (Else the conenction gets deleted after the function was called.)
153 * @param func the function which hsould be called on new conenctions.
155 void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
156 const NewConnectionBaseCallbackFunc& func)
158 m_new_connection_base_callback= func;
159 } // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
164 * @brief callback for new connections.
166 * The base method always returns an empty pointer.
168 * Derived classes must override this method and do something useful with
169 * the passed file descriptor.
171 * @param fd the file descriptor of the new connection.
172 * @param addr pointer to the address structure filled from ::accept()
173 * @return shared pointer to the new IO class instance (; empty if not accepted)
175 IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
176 int fd, boost::any addr)
178 // needs to be defined in derived class!
179 return IOImplementationPtr();
180 } // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
185 ** implementation of UnixIOSocket
189 UnixIOSocket::UnixIOSocket()
194 } // eo UnixIOSocket::UnixIOSocket()
198 UnixIOSocket::UnixIOSocket(const std::string& path)
204 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
207 UnixIOSocket::UnixIOSocket(
208 int fd, const std::string& path,
209 unsigned int peer_pid, unsigned int peer_uid, unsigned int peer_gid)
210 : IOImplementation(fd,fd)
212 , m_peer_pid(peer_pid)
213 , m_peer_uid(peer_uid)
214 , m_peer_gid(peer_gid)
216 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
220 * @brief opens a (client) connection to an unix domain socket.
222 * @param path the path the server is listening on.
223 * @return @a true iff the connection was successfully opened.
225 bool UnixIOSocket::open(const std::string& path)
227 if (opened()) close();
232 if (path.empty() || path.size() >= PATH_MAX)
237 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
246 addr.m_addr_un.sun_family= AF_UNIX;
247 strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX);
248 if (::connect(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)
259 } // eo UnixIOSocket::open(const std::string&,int)
263 ** implementation of UnixServerSocketBase
267 UnixServerSocketBase::UnixServerSocketBase()
269 } // eo UnixServerSocketBase::UnixServerSocketBase
272 UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
275 } // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
279 * @brief opens the server part of an unix domain socket.
281 * @param path the path the new port should listen on.
282 * @param mode the mode for the path.
283 * @return @a true iff the port was successfully opened.
285 bool UnixServerSocketBase::open(const std::string& path, int mode)
287 if (opened()) close();
289 if (path.empty() || path.size() >= PATH_MAX)
294 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
303 addr.m_addr_un.sun_family= AF_UNIX;
304 strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX);
305 Utils::unlink(path); // just in case...
306 mode_t old_mask= ::umask( (mode & 0777) ^ 0777);
307 if (::bind(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)
319 int res= ::listen(fd,8);
329 } // eo UnixServerSocketBase::open(const std::string&,int)
333 * @brief called from base class to create a new connection instance.
335 * This method accepts only connections to unix domain sockets.
336 * It also tries to determine the peer pid, uid and gid.
338 * @param fd the file descriptor of a freshly accepted connection.
339 * @param addr conatins "pointer to struct sockaddr"
340 * @return @a a (shared) pointer to the new connection isntance; empty if none was
343 IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
345 struct sockaddr *addr_ptr= NULL;
347 addr_ptr = boost::any_cast<struct sockaddr*>(addr);
349 catch (boost::bad_any_cast&)
351 return IOImplementationPtr();
353 // check for the right family:
354 if (addr_ptr->sa_family != AF_UNIX)
356 return IOImplementationPtr();
358 struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
359 std::string peer_path( un_ptr->sun_path );
364 { // the linux way to get peer info (pid,gid,uid):
366 socklen_t cred_len = sizeof(cred);
367 if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
375 #error dont know how to determine peer info.
378 UnixIOSocketPtr ptr( createIOSocket(fd, peer_path, peer_pid, peer_uid, peer_gid) );
380 } // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
384 * @brief "real" creator of the connection instance.
386 * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
388 * @param fd file descriptor for the socket
389 * @param path path as delivered by peer.
390 * @param peer_pid peer pid.
391 * @param peer_uid peer uid.
392 * @param peer_gid peer gid.
393 * @return (shared) pointer to the new io instance.
395 UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
396 int fd, const std::string& path,
397 unsigned int peer_pid,
398 unsigned int peer_uid, unsigned int peer_gid)
400 return UnixIOSocketPtr(
401 new UnixIOSocket(fd, path, peer_pid, peer_uid, peer_gid)
403 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
407 }// eo namespace AsyncIo