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