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