468ceb3ff20563dab391fd24d897820b1f09e068
[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 : m_peer_pid(0)
181 , m_peer_uid(0)
182 , m_peer_gid(0)
183 {
184 } // eo UnixIOSocket::UnixIOSocket()
185
186
187
188 UnixIOSocket::UnixIOSocket(const std::string& path)
189 : m_peer_pid(0)
190 , m_peer_uid(0)
191 , m_peer_gid(0)
192 {
193    open(path);
194 } // eo UnixIOSocket::UnixIOSocket(const std::string&)
195
196
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)
201 , m_path(path)
202 , m_peer_pid(peer_pid)
203 , m_peer_uid(peer_uid)
204 , m_peer_gid(peer_gid)
205 {
206 } // eo UnixIOSocket::UnixIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
207
208
209 /**
210  * @brief opens a (client) connection to an unix domain socket.
211  *
212  * @param path the path the server is listening on.
213  * @return @a true iff the connection was successfully opened.
214  */
215 bool UnixIOSocket::open(const std::string& path)
216 {
217    if (opened()) close();
218
219    m_errno= 0;
220    m_path.clear();
221
222    if (path.empty() || path.size() >= PATH_MAX)
223    {
224       return false;
225    }
226
227    int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
228    if (fd<0)
229    {
230       m_errno= errno;
231       return false;
232    }
233
234    {
235       MegaAddr addr;
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)
239       {
240          m_errno= errno;
241          ::close(fd);
242          return false;
243       }
244    }
245    m_path= path;
246    setReadFd(fd);
247    setWriteFd(fd);
248    return true;
249 } // eo UnixIOSocket::open(const std::string&,int)
250
251
252 /*
253 ** implementation of UnixServerSocketBase
254 */
255
256
257 UnixServerSocketBase::UnixServerSocketBase()
258 {
259 } // eo UnixServerSocketBase::UnixServerSocketBase
260
261
262 UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
263 {
264    open(path,mode);
265 } // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
266
267
268 /**
269  * @brief opens the server part of an unix domain socket.
270  *
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.
274  */
275 bool UnixServerSocketBase::open(const std::string& path, int mode)
276 {
277    if (opened()) close();
278    m_errno= 0;
279    if (path.empty() || path.size() >= PATH_MAX)
280    {
281       return false;
282    }
283
284    int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
285    if (fd<0)
286    {
287       m_errno= errno;
288       return false;
289    }
290
291    {
292       MegaAddr addr;
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)
298       {
299          m_errno= errno;
300          ::umask(old_mask);
301          ::close(fd);
302          return false;
303       }
304       ::umask(old_mask);
305    }
306    m_path= path;
307
308    {
309       int res= ::listen(fd,8);
310       if (res < 0)
311       {
312          m_errno= errno;
313          ::close(fd);
314          return false;
315       }
316    }
317    setReadFd(fd);
318    return true;
319 } // eo UnixServerSocketBase::open(const std::string&,int)
320
321
322 /**
323  * @brief called from base class to create a new connection instance.
324  *
325  * This method accepts only connections to unix domain sockets.
326  * It also tries to determine the peer pid, uid and gid.
327  *
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
331  * created.
332  */
333 IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
334 {
335    struct sockaddr *addr_ptr= NULL;
336    try {
337       addr_ptr = boost::any_cast<struct sockaddr*>(addr);
338    }
339    catch (boost::bad_any_cast&)
340    {
341       return IOImplementationPtr();
342    }
343    // check for the right family:
344    if (addr_ptr->sa_family != AF_UNIX)
345    {
346       return IOImplementationPtr();
347    }
348    struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
349    std::string peer_path( un_ptr->sun_path );
350    unsigned peer_pid=0;
351    unsigned peer_gid=0;
352    unsigned peer_uid=0;
353 #ifdef __linux__
354    { // the linux way to get peer info (pid,gid,uid):
355       struct ucred cred;
356       socklen_t cred_len = sizeof(cred);
357       if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
358       {
359          peer_pid= cred.pid;
360          peer_uid= cred.uid;
361          peer_gid= cred.gid;
362       }
363    }
364 #else
365 #error dont know how to determine peer info.
366 #endif
367
368    UnixIOSocketPtr ptr( createIOSocket(fd, peer_path, peer_pid, peer_uid, peer_gid) );
369    return ptr;
370 } // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
371
372
373 /**
374  * @brief "real" creator of the connection instance.
375  *
376  * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
377  *
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.
384  */
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)
389 {
390    return UnixIOSocketPtr(
391       new UnixIOSocket(fd, path, peer_pid, peer_uid, peer_gid)
392    );
393 } // eo UnixServerSocketBase::createIOSocket(int,const std::string&,unsigned,unsigned,unsigned)
394
395
396
397 }// eo namespace AsyncIo