updated to use new modules (more independance to libi2ncommon)
[libasyncio] / asyncio / async_socket.cpp
1 /** @file
2  *
3  * (c) Copyright 2008 by Intra2net AG
4  * 
5  * info@intra2net.com
6  *
7  * @todo unlink unix server socket on close.
8  */
9
10 #include "async_socket.hpp"
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <sys/socket.h>
14 #include <errno.h>
15 #include <sys/un.h>
16 #include <netinet/in.h>
17 #include <limits.h>
18 #include <asyncio_system_tools.cpp>
19
20
21 namespace AsyncIo
22 {
23
24
25 namespace
26 {
27
28 struct sockaddr_un dummy_un;
29
30 /**
31     Struct for holding sockaddr.
32
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.
36
37     This works as long as the POSIX functions don't verify the buffer length.
38     For glibc on linux this is true.
39 */
40 union MegaAddr {
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 ];
49 };
50
51
52 } // eo namespace <anonymous>
53
54
55
56 /*
57 ** implementation of ServerSocketBaseImplementation
58 */
59
60
61 ServerSocketBaseImplementation::ServerSocketBaseImplementation()
62 : IOImplementation()
63 {
64 } // eo ServerSocketBaseImplementation::ServerSocketBaseImplementation()
65
66
67 /**
68  * @brief handled incoming connections on the server port.
69  *
70  * accepts the new connection, stores the peer address in an internal buffer
71  * and calls a (derived) acceptNewConnection method to create an apropriate
72  * IO class instance.
73  * If no io instance is created the connection is closed.
74  */
75 void ServerSocketBaseImplementation::doRead()
76 {
77    MegaAddr addr;
78    socklen_t addrlen = sizeof(addr);
79
80    // reset errno:
81    m_errno= 0;
82
83    // reset the mark:
84    resetReadMark();
85
86    int fd = ::accept( readFd(), &addr.m_addr, &addrlen);
87    if (fd < 0)
88    {
89       // handle errors.
90       m_errno= errno;
91       switch (m_errno)
92       {
93          case EBADF:
94          case EINVAL:
95          case ENOTSOCK:
96             // should not happen...
97             close();
98             break;
99          default:;
100       }
101       return;
102    }
103
104    if (addrlen < sizeof(addr))
105    {
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;
109    }
110    else
111    {
112       //something went terribly wrong!!
113       // the resulting address structure is larger than it ever could be...
114       ::close(fd);
115       return;
116    }
117
118    IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
119    if(!connection)
120    {
121       ::close(fd);
122       return;
123    }
124    if (m_new_connection_base_callback)
125    {
126       m_new_connection_base_callback(connection);
127    }
128 } // eo ServerSocketBaseImplementation::doRead()
129
130
131 /**
132  * @brief handles write events.
133  *
134  * since server sockets never ever get a write mark, something must
135  * be wrong and the connection is closed!
136  */
137 void ServerSocketBaseImplementation::doWrite()
138 {
139    // if this method is called, something went wrong...
140    close();
141    //TODO throw something?!
142 } // eo ServerSocketBaseImplementation::doWrite()
143
144
145
146 /**
147  * @brief sets a function which is called when a new connection was established.
148  *
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.)
152  *
153  * @param func the function which hsould be called on new conenctions.
154  */
155 void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
156    const NewConnectionBaseCallbackFunc& func)
157 {
158    m_new_connection_base_callback= func;
159 } // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
160
161
162
163 /**
164  * @brief callback for new connections.
165  *
166  * The base method always returns an empty pointer.
167  *
168  * Derived classes must override this method and do something useful with
169  * the passed file descriptor.
170  *
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)
174  */
175 IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
176    int fd, boost::any addr)
177 {
178    // needs to be defined in derived class!
179    return IOImplementationPtr();
180 } // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
181
182
183
184 /*
185 ** implementation of UnixIOSocket
186 */
187
188
189 UnixIOSocket::UnixIOSocket()
190 : m_peer_pid(0)
191 , m_peer_uid(0)
192 , m_peer_gid(0)
193 {
194 } // eo UnixIOSocket::UnixIOSocket()
195
196
197
198 UnixIOSocket::UnixIOSocket(const std::string& path)
199 : m_peer_pid(0)
200 , m_peer_uid(0)
201 , m_peer_gid(0)
202 {
203    open(path);
204 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
205
206
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)
211 , m_path(path)
212 , m_peer_pid(peer_pid)
213 , m_peer_uid(peer_uid)
214 , m_peer_gid(peer_gid)
215 {
216 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
217
218
219 /**
220  * @brief opens a (client) connection to an unix domain socket.
221  *
222  * @param path the path the server is listening on.
223  * @return @a true iff the connection was successfully opened.
224  */
225 bool UnixIOSocket::open(const std::string& path)
226 {
227    if (opened()) close();
228
229    m_errno= 0;
230    m_path.clear();
231
232    if (path.empty() || path.size() >= PATH_MAX)
233    {
234       return false;
235    }
236
237    int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
238    if (fd<0)
239    {
240       m_errno= errno;
241       return false;
242    }
243
244    {
245       MegaAddr addr;
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)
249       {
250          m_errno= errno;
251          ::close(fd);
252          return false;
253       }
254    }
255    m_path= path;
256    setReadFd(fd);
257    setWriteFd(fd);
258    return true;
259 } // eo UnixIOSocket::open(const std::string&,int)
260
261
262 /*
263 ** implementation of UnixServerSocketBase
264 */
265
266
267 UnixServerSocketBase::UnixServerSocketBase()
268 {
269 } // eo UnixServerSocketBase::UnixServerSocketBase
270
271
272 UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
273 {
274    open(path,mode);
275 } // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
276
277
278 /**
279  * @brief opens the server part of an unix domain socket.
280  *
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.
284  */
285 bool UnixServerSocketBase::open(const std::string& path, int mode)
286 {
287    if (opened()) close();
288    m_errno= 0;
289    if (path.empty() || path.size() >= PATH_MAX)
290    {
291       return false;
292    }
293
294    int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
295    if (fd<0)
296    {
297       m_errno= errno;
298       return false;
299    }
300
301    {
302       MegaAddr addr;
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)
308       {
309          m_errno= errno;
310          ::umask(old_mask);
311          ::close(fd);
312          return false;
313       }
314       ::umask(old_mask);
315    }
316    m_path= path;
317
318    {
319       int res= ::listen(fd,8);
320       if (res < 0)
321       {
322          m_errno= errno;
323          ::close(fd);
324          return false;
325       }
326    }
327    setReadFd(fd);
328    return true;
329 } // eo UnixServerSocketBase::open(const std::string&,int)
330
331
332 /**
333  * @brief called from base class to create a new connection instance.
334  *
335  * This method accepts only connections to unix domain sockets.
336  * It also tries to determine the peer pid, uid and gid.
337  *
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
341  * created.
342  */
343 IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
344 {
345    struct sockaddr *addr_ptr= NULL;
346    try {
347       addr_ptr = boost::any_cast<struct sockaddr*>(addr);
348    }
349    catch (boost::bad_any_cast&)
350    {
351       return IOImplementationPtr();
352    }
353    // check for the right family:
354    if (addr_ptr->sa_family != AF_UNIX)
355    {
356       return IOImplementationPtr();
357    }
358    struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
359    std::string peer_path( un_ptr->sun_path );
360    unsigned peer_pid=0;
361    unsigned peer_gid=0;
362    unsigned peer_uid=0;
363 #ifdef __linux__
364    { // the linux way to get peer info (pid,gid,uid):
365       struct ucred cred;
366       socklen_t cred_len = sizeof(cred);
367       if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
368       {
369          peer_pid= cred.pid;
370          peer_uid= cred.uid;
371          peer_gid= cred.gid;
372       }
373    }
374 #else
375 #error dont know how to determine peer info.
376 #endif
377
378    UnixIOSocketPtr ptr( createIOSocket(fd, peer_path, peer_pid, peer_uid, peer_gid) );
379    return ptr;
380 } // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
381
382
383 /**
384  * @brief "real" creator of the connection instance.
385  *
386  * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
387  *
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.
394  */
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)
399 {
400    return UnixIOSocketPtr(
401       new UnixIOSocket(fd, path, peer_pid, peer_uid, peer_gid)
402    );
403 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
404
405
406
407 }// eo namespace AsyncIo