PC-lint: Silence warning about buffer possible overflow - we do this by design :)
[libasyncio] / asyncio / async_socket.cpp
CommitLineData
8c15b8c7
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
5c8a3d40
RP
20/** @file
21 *
6ac1fb46
RP
22 * @copyright Copyright 2008-2009 by Intra2net AG
23 * @contact Intra2net Opensource Team \<opensource@intra2net.com\>
5c8a3d40
RP
24 *
25 * @todo unlink unix server socket on close.
26 */
27
42b7c46d 28#include "async_socket.hpp"
5c8a3d40
RP
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <sys/socket.h>
32#include <errno.h>
33#include <sys/un.h>
34#include <netinet/in.h>
35#include <limits.h>
fd6ea89e 36#include <asyncio_system_tools.cpp>
5c8a3d40
RP
37
38
42b7c46d 39namespace AsyncIo
5c8a3d40
RP
40{
41
42
43namespace
44{
45
46struct sockaddr_un dummy_un;
47
e4e9cd8c
TJ
48/**
49 Struct for holding sockaddr.
50
51 It's called "MegaAddr" because we use a trick to extend the historical UNIX domain socket
52 path length of 108 bytes to PATH_MAX by declaring a large enough buffer
53 at the end of the union.
54
55 This works as long as the POSIX functions don't verify the buffer length.
56 For glibc on linux this is true.
57*/
5c8a3d40
RP
58union MegaAddr {
59 struct sockaddr m_addr;
60 struct sockaddr_in m_addr_in;
61 struct sockaddr_un m_addr_un; // NOTE (historically) too small...
62 // storage is large enough to hold all sockaddr_* variants with the (historical) exception of _un !
63 struct sockaddr_storage m_addr_store;
64 // a char array large enough to hold _un (with an path up to the maximum allowed size!)
65 // (the +1 is added for a later 0-termination of the path)
66 char m_buffer[ sizeof(dummy_un) - sizeof(dummy_un.sun_path) + PATH_MAX + 1 ];
67};
68
69
70} // eo namespace <anonymous>
71
72
73
74/*
75** implementation of ServerSocketBaseImplementation
76*/
77
78
79ServerSocketBaseImplementation::ServerSocketBaseImplementation()
80: IOImplementation()
81{
82} // eo ServerSocketBaseImplementation::ServerSocketBaseImplementation()
83
84
85/**
86 * @brief handled incoming connections on the server port.
87 *
88 * accepts the new connection, stores the peer address in an internal buffer
6ac1fb46 89 * and calls a (derived) acceptNewConnection method to create an appropriate
5c8a3d40
RP
90 * IO class instance.
91 * If no io instance is created the connection is closed.
92 */
93void ServerSocketBaseImplementation::doRead()
94{
95 MegaAddr addr;
96 socklen_t addrlen = sizeof(addr);
97
98 // reset errno:
99 m_errno= 0;
100
101 // reset the mark:
102 resetReadMark();
103
104 int fd = ::accept( readFd(), &addr.m_addr, &addrlen);
105 if (fd < 0)
106 {
107 // handle errors.
108 m_errno= errno;
109 switch (m_errno)
110 {
111 case EBADF:
112 case EINVAL:
113 case ENOTSOCK:
114 // should not happen...
115 close();
116 break;
117 default:;
118 }
119 return;
120 }
121
122 if (addrlen < sizeof(addr))
123 {
124 // in case of unix domain socket: terminate the path!
125 // NOTE we are doing this here since we don't pass the length info.
126 addr.m_buffer[addrlen]= 0;
127 }
128 else
129 {
130 //something went terribly wrong!!
131 // the resulting address structure is larger than it ever could be...
132 ::close(fd);
133 return;
134 }
135
136 IOImplementationPtr connection= acceptNewConnection(fd, &addr.m_addr);
137 if(!connection)
138 {
139 ::close(fd);
140 return;
141 }
142 if (m_new_connection_base_callback)
143 {
144 m_new_connection_base_callback(connection);
145 }
146} // eo ServerSocketBaseImplementation::doRead()
147
148
149/**
150 * @brief handles write events.
151 *
152 * since server sockets never ever get a write mark, something must
153 * be wrong and the connection is closed!
154 */
155void ServerSocketBaseImplementation::doWrite()
156{
157 // if this method is called, something went wrong...
158 close();
159 //TODO throw something?!
160} // eo ServerSocketBaseImplementation::doWrite()
161
162
163
164/**
165 * @brief sets a function which is called when a new connection was established.
166 *
6ac1fb46 167 * The function gets a (shared) pointer to the new connection as parameter and is
5c8a3d40 168 * expected to store it when it accepts the connection.
6ac1fb46 169 * (Else the connection gets deleted after the function was called.)
5c8a3d40 170 *
6ac1fb46 171 * @param func the function which should be called on new connections.
5c8a3d40
RP
172 */
173void ServerSocketBaseImplementation::setNewConnectionBaseCallback(
174 const NewConnectionBaseCallbackFunc& func)
175{
176 m_new_connection_base_callback= func;
177} // eo ServerSocketBaseImplementation::setNewConnectionBaseCallback(NewConnectionBaseCallbackFunc&)
178
179
180
181/**
182 * @brief callback for new connections.
183 *
184 * The base method always returns an empty pointer.
185 *
186 * Derived classes must override this method and do something useful with
187 * the passed file descriptor.
188 *
189 * @param fd the file descriptor of the new connection.
190 * @param addr pointer to the address structure filled from ::accept()
191 * @return shared pointer to the new IO class instance (; empty if not accepted)
192 */
193IOImplementationPtr ServerSocketBaseImplementation::acceptNewConnection(
194 int fd, boost::any addr)
195{
196 // needs to be defined in derived class!
197 return IOImplementationPtr();
198} // eo ServerSocketBaseImplementation::acceptNewConnection(int,boost::any)
199
200
201
202/*
203** implementation of UnixIOSocket
204*/
205
206
207UnixIOSocket::UnixIOSocket()
6ac1fb46
RP
208: m_peer_pid((unsigned int)-1)
209, m_peer_uid((unsigned int)-1)
210, m_peer_gid((unsigned int)-1)
5c8a3d40
RP
211{
212} // eo UnixIOSocket::UnixIOSocket()
213
214
215
216UnixIOSocket::UnixIOSocket(const std::string& path)
6ac1fb46
RP
217: m_peer_pid((unsigned int)-1)
218, m_peer_uid((unsigned int)-1)
219, m_peer_gid((unsigned int)-1)
5c8a3d40 220{
6ac1fb46 221 open(path);
5c8a3d40
RP
222} // eo UnixIOSocket::UnixIOSocket(const std::string&)
223
224
6ac1fb46 225UnixIOSocket::UnixIOSocket(int fd,const std::string& path)
5c8a3d40
RP
226: IOImplementation(fd,fd)
227, m_path(path)
6ac1fb46
RP
228, m_peer_pid((unsigned int)-1)
229, m_peer_uid((unsigned int)-1)
230, m_peer_gid((unsigned int)-1)
5c8a3d40 231{
6ac1fb46
RP
232 update_peer_information(fd);
233} // eo UnixIOSocket::UnixIOSocket(int,const std::string&)
5c8a3d40
RP
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 */
242bool 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;
1262d853
TJ
264 strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX); //lint !e419
265 if (::connect(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0) //lint !e413
5c8a3d40
RP
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
6ac1fb46
RP
279/**
280 * @brief update the peer info (pid,gid,uid).
281 * @param fd the socket to update the information for.
282 * @return @a true if succesfully determined the peer info.
283 *
284 * @note if the peer info could not be detected the values are set to
285 * (unsigned int)(-1).
286 */
287bool UnixIOSocket::update_peer_information(int fd)
288{
289 m_peer_pid=(unsigned int)-1;
290 m_peer_gid=(unsigned int)-1;
291 m_peer_uid=(unsigned int)-1;
292
293 //TODO add more versions of getting the peer information here...
294 // BSD systems seems to have SO_PEERCRED,too (does someone know how it is used there?)
295
296 #ifdef __linux__
297 { // the linux way to get peer info (pid,gid,uid):
298 struct ucred cred;
299 socklen_t cred_len = sizeof(cred);
300 if (getsockopt(fd,SOL_SOCKET,SO_PEERCRED,&cred,&cred_len) == 0)
301 {
302 m_peer_pid= cred.pid;
303 m_peer_uid= cred.uid;
304 m_peer_gid= cred.gid;
305 return true;
306 }
307 }
308 #else
309 #warning dont know how to determine peer info.
310 #endif
311 return false;
312} // end of UnixIOSocketupdate_peer_information(int)
313
314
5c8a3d40
RP
315/*
316** implementation of UnixServerSocketBase
317*/
318
319
320UnixServerSocketBase::UnixServerSocketBase()
321{
322} // eo UnixServerSocketBase::UnixServerSocketBase
323
324
325UnixServerSocketBase::UnixServerSocketBase(const std::string& path, int mode)
326{
327 open(path,mode);
328} // eo UnixServerSocketBase::UnixServerSocketBase(const std::string&,int)
329
330
331/**
332 * @brief opens the server part of an unix domain socket.
333 *
334 * @param path the path the new port should listen on.
335 * @param mode the mode for the path.
336 * @return @a true iff the port was successfully opened.
337 */
338bool UnixServerSocketBase::open(const std::string& path, int mode)
339{
340 if (opened()) close();
341 m_errno= 0;
342 if (path.empty() || path.size() >= PATH_MAX)
343 {
344 return false;
345 }
346
347 int fd= ::socket(PF_UNIX, SOCK_STREAM, 0);
348 if (fd<0)
349 {
350 m_errno= errno;
351 return false;
352 }
353
354 {
355 MegaAddr addr;
356 addr.m_addr_un.sun_family= AF_UNIX;
1262d853 357 strncpy(addr.m_addr_un.sun_path, path.c_str(), PATH_MAX); //lint !e419
fd6ea89e 358 Utils::unlink(path); // just in case...
6ac1fb46
RP
359 // NOTE this is a place which might require some updates for multithreaded
360 // usage! (setting the umask affects all threads...)
5c8a3d40 361 mode_t old_mask= ::umask( (mode & 0777) ^ 0777);
1262d853 362 if (::bind(fd,(sockaddr*)&addr.m_addr_un, SUN_LEN(&addr.m_addr_un)) < 0) //lint !e413
5c8a3d40
RP
363 {
364 m_errno= errno;
365 ::umask(old_mask);
366 ::close(fd);
367 return false;
368 }
369 ::umask(old_mask);
370 }
371 m_path= path;
372
373 {
374 int res= ::listen(fd,8);
375 if (res < 0)
376 {
377 m_errno= errno;
378 ::close(fd);
379 return false;
380 }
381 }
382 setReadFd(fd);
383 return true;
384} // eo UnixServerSocketBase::open(const std::string&,int)
385
386
387/**
388 * @brief called from base class to create a new connection instance.
389 *
390 * This method accepts only connections to unix domain sockets.
391 * It also tries to determine the peer pid, uid and gid.
392 *
393 * @param fd the file descriptor of a freshly accepted connection.
6ac1fb46
RP
394 * @param addr contains "pointer to struct sockaddr"
395 * @return @a a (shared) pointer to the new connection instance; empty if none was
5c8a3d40
RP
396 * created.
397 */
398IOImplementationPtr UnixServerSocketBase::acceptNewConnection(int fd, boost::any addr)
399{
400 struct sockaddr *addr_ptr= NULL;
401 try {
402 addr_ptr = boost::any_cast<struct sockaddr*>(addr);
403 }
404 catch (boost::bad_any_cast&)
405 {
406 return IOImplementationPtr();
407 }
408 // check for the right family:
409 if (addr_ptr->sa_family != AF_UNIX)
410 {
411 return IOImplementationPtr();
412 }
413 struct sockaddr_un *un_ptr = reinterpret_cast<struct sockaddr_un*>(addr_ptr);
414 std::string peer_path( un_ptr->sun_path );
5c8a3d40 415
6ac1fb46
RP
416 UnixIOSocketPtr ptr( createIOSocket(fd, peer_path) );
417
5c8a3d40
RP
418 return ptr;
419} // eo UnixServerSocketBase::acceptNewConnection(int,boost::any);
420
421
422/**
423 * @brief "real" creator of the connection instance.
424 *
425 * called by UnixServerSocketBase::acceptNewConnection to create the new io instance.
426 *
427 * @param fd file descriptor for the socket
428 * @param path path as delivered by peer.
5c8a3d40
RP
429 * @return (shared) pointer to the new io instance.
430 */
431UnixIOSocketPtr UnixServerSocketBase::createIOSocket(
6ac1fb46
RP
432 int fd, const std::string& path
433)
5c8a3d40 434{
6ac1fb46
RP
435 return UnixIOSocketPtr
436 (
437 new UnixIOSocket(fd, path)
5c8a3d40 438 );
6ac1fb46 439} // eo UnixServerSocketBase::createIOSocket(int,const std::string&)
5c8a3d40
RP
440
441
442
42b7c46d 443}// eo namespace AsyncIo