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