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 <filefunc.hxx>
28 struct sockaddr_un dummy_un;
31 struct sockaddr m_addr;
32 struct sockaddr_in m_addr_in;
33 struct sockaddr_un m_addr_un; // NOTE (historically) too small...
34 // storage is large enough to hold all sockaddr_* variants with the (historical) exception of _un !
35 struct sockaddr_storage m_addr_store;
36 // a char array large enough to hold _un (with an path up to the maximum allowed size!)
37 // (the +1 is added for a later 0-termination of the path)
38 char m_buffer[ sizeof(dummy_un) - sizeof(dummy_un.sun_path) + PATH_MAX + 1 ];
42 } // eo namespace <anonymous>
47 ** implementation of ServerSocketBaseImplementation
51 ServerSocketBaseImplementation::ServerSocketBaseImplementation()
54 } // eo ServerSocketBaseImplementation::ServerSocketBaseImplementation()
58 * @brief handled incoming connections on the server port.
60 * accepts the new connection, stores the peer address in an internal buffer
61 * and calls a (derived) acceptNewConnection method to create an apropriate
63 * If no io instance is created the connection is closed.
65 void ServerSocketBaseImplementation::doRead()
68 socklen_t addrlen = sizeof(addr);
76 int fd = ::accept( readFd(), &addr.m_addr, &addrlen);
86 // should not happen...
94 if (addrlen < sizeof(addr))
96 // in case of unix domain socket: terminate the path!
97 // NOTE we are doing this here since we don't pass the length info.
98 addr.m_buffer[addrlen]= 0;
102 //something went terribly wrong!!
103 // the resulting address structure is larger than it ever could be...
108 IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
114 if (m_new_connection_base_callback)
116 m_new_connection_base_callback(connection);
118 } // eo ServerSocketBaseImplementation::doRead()
122 * @brief handles write events.
124 * since server sockets never ever get a write mark, something must
125 * be wrong and the connection is closed!
127 void ServerSocketBaseImplementation::doWrite()
129 // if this method is called, something went wrong...
131 //TODO throw something?!
132 } // eo ServerSocketBaseImplementation::doWrite()
137 * @brief sets a function which is called when a new connection was established.
139 * The function gets a (shared) pointer to the new connetion as parameter and is
140 * expected to store it when it accepts the connection.
141 * (Else the conenction gets deleted after the function was called.)
143 * @param func the function which hsould be called on new conenctions.
145 void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
146 const NewConnectionBaseCallbackFunc& func)
148 m_new_connection_base_callback= func;
149 } // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
154 * @brief callback for new connections.
156 * The base method always returns an empty pointer.
158 * Derived classes must override this method and do something useful with
159 * the passed file descriptor.
161 * @param fd the file descriptor of the new connection.
162 * @param addr pointer to the address structure filled from ::accept()
163 * @return shared pointer to the new IO class instance (; empty if not accepted)
165 IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
166 int fd, boost::any addr)
168 // needs to be defined in derived class!
169 return IOImplementationPtr();
170 } // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
175 ** implementation of UnixIOSocket
179 UnixIOSocket::UnixIOSocket()
184 } // eo UnixIOSocket::UnixIOSocket()
188 UnixIOSocket::UnixIOSocket(const std::string& path)
194 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
197 UnixIOSocket::UnixIOSocket(
198 int fd, const std::string& path,
199 unsigned int peer_pid, unsigned int peer_uid, unsigned int peer_gid)
200 : IOImplementation(fd,fd)
202 , m_peer_pid(peer_pid)
203 , m_peer_uid(peer_uid)
204 , m_peer_gid(peer_gid)
206 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
210 * @brief opens a (client) connection to an unix domain socket.
212 * @param path the path the server is listening on.
213 * @return @a true iff the connection was successfully opened.
215 bool UnixIOSocket::open(const std::string& path)
217 if (opened()) close();
222 if (path.empty() || path.size() >= PATH_MAX)
227 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
236 addr.m_addr_un.sun_family= AF_UNIX;
237 strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX);
238 if (::connect(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)
249 } // eo UnixIOSocket::open(const std::string&,int)
253 ** implementation of UnixServerSocketBase
257 UnixServerSocketBase::UnixServerSocketBase()
259 } // eo UnixServerSocketBase::UnixServerSocketBase
262 UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
265 } // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
269 * @brief opens the server part of an unix domain socket.
271 * @param path the path the new port should listen on.
272 * @param mode the mode for the path.
273 * @return @a true iff the port was successfully opened.
275 bool UnixServerSocketBase::open(const std::string& path, int mode)
277 if (opened()) close();
279 if (path.empty() || path.size() >= PATH_MAX)
284 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
293 addr.m_addr_un.sun_family= AF_UNIX;
294 strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX);
295 ::I2n::unlink(path); // just in case...
296 mode_t old_mask= ::umask( (mode & 0777) ^ 0777);
297 if (::bind(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0)
309 int res= ::listen(fd,8);
319 } // eo UnixServerSocketBase::open(const std::string&,int)
323 * @brief called from base class to create a new connection instance.
325 * This method accepts only connections to unix domain sockets.
326 * It also tries to determine the peer pid, uid and gid.
328 * @param fd the file descriptor of a freshly accepted connection.
329 * @param addr conatins "pointer to struct sockaddr"
330 * @return @a a (shared) pointer to the new connection isntance; empty if none was
333 IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
335 struct sockaddr *addr_ptr= NULL;
337 addr_ptr = boost::any_cast<struct sockaddr*>(addr);
339 catch (boost::bad_any_cast&)
341 return IOImplementationPtr();
343 // check for the right family:
344 if (addr_ptr->sa_family != AF_UNIX)
346 return IOImplementationPtr();
348 struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
349 std::string peer_path( un_ptr->sun_path );
354 { // the linux way to get peer info (pid,gid,uid):
356 socklen_t cred_len = sizeof(cred);
357 if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
365 #error dont know how to determine peer info.
368 UnixIOSocketPtr ptr( createIOSocket(fd, peer_path, peer_pid, peer_uid, peer_gid) );
370 } // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
374 * @brief "real" creator of the connection instance.
376 * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
378 * @param fd file descriptor for the socket
379 * @param path path as delivered by peer.
380 * @param peer_pid peer pid.
381 * @param peer_uid peer uid.
382 * @param peer_gid peer gid.
383 * @return (shared) pointer to the new io instance.
385 UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
386 int fd, const std::string& path,
387 unsigned int peer_pid,
388 unsigned int peer_uid, unsigned int peer_gid)
390 return UnixIOSocketPtr(
391 new UnixIOSocket(fd, path, peer_pid, peer_uid, peer_gid)
393 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
397 }// eo namespace AsyncIo