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