libasyncio: (tomj) fix uninitialized vars, added little safety against zombie filters
[libasyncio] / asyncio / async_socket.cpp
CommitLineData
5c8a3d40
RP
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
42b7c46d 10#include "async_socket.hpp"
5c8a3d40
RP
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
42b7c46d 21namespace AsyncIo
5c8a3d40
RP
22{
23
24
25namespace
26{
27
28struct sockaddr_un dummy_un;
29
30union 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
51ServerSocketBaseImplementation::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 */
65void 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 */
127void 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 */
145void 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 */
165IOImplementationPtr 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
179UnixIOSocket::UnixIOSocket()
9f5d794e
TJ
180: m_peer_pid(0)
181, m_peer_uid(0)
182, m_peer_gid(0)
5c8a3d40
RP
183{
184} // eo UnixIOSocket::UnixIOSocket()
185
186
187
188UnixIOSocket::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
197UnixIOSocket::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 */
215bool 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
257UnixServerSocketBase::UnixServerSocketBase()
258{
259} // eo UnixServerSocketBase::UnixServerSocketBase
260
261
262UnixServerSocketBase::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 */
275bool 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 */
333IOImplementationPtr 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 */
385UnixIOSocketPtr 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
42b7c46d 397}// eo namespace AsyncIo