]> granicus.if.org Git - icinga2/blob - lib/base/socket.cpp
Update copyright headers for 2016
[icinga2] / lib / base / socket.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/socket.hpp"
21 #include "base/objectlock.hpp"
22 #include "base/utility.hpp"
23 #include "base/exception.hpp"
24 #include "base/logger.hpp"
25 #include <sstream>
26 #include <iostream>
27 #include <boost/exception/errinfo_api_function.hpp>
28 #include <boost/exception/errinfo_errno.hpp>
29 #include <socketpair.h>
30
31 #ifndef _WIN32
32 #       include <poll.h>
33 #endif /* _WIN32 */
34
35 using namespace icinga;
36
37 /**
38  * Constructor for the Socket class.
39  */
40 Socket::Socket(void)
41         : m_FD(INVALID_SOCKET)
42 { }
43
44 /**
45  * Constructor for the Socket class.
46  */
47 Socket::Socket(SOCKET fd)
48         : m_FD(INVALID_SOCKET)
49 {
50         SetFD(fd);
51 }
52
53 /**
54  * Destructor for the Socket class.
55  */
56 Socket::~Socket(void)
57 {
58         Close();
59 }
60
61 /**
62  * Sets the file descriptor for this socket object.
63  *
64  * @param fd The file descriptor.
65  */
66 void Socket::SetFD(SOCKET fd)
67 {
68         if (fd != INVALID_SOCKET) {
69 #ifndef _WIN32
70                 /* mark the socket as close-on-exec */
71                 Utility::SetCloExec(fd);
72 #endif /* _WIN32 */
73         }
74
75         ObjectLock olock(this);
76         m_FD = fd;
77 }
78
79 /**
80  * Retrieves the file descriptor for this socket object.
81  *
82  * @returns The file descriptor.
83  */
84 SOCKET Socket::GetFD(void) const
85 {
86         ObjectLock olock(this);
87
88         return m_FD;
89 }
90
91 /**
92  * Closes the socket.
93  */
94 void Socket::Close(void)
95 {
96         ObjectLock olock(this);
97
98         if (m_FD != INVALID_SOCKET) {
99                 closesocket(m_FD);
100                 m_FD = INVALID_SOCKET;
101         }
102 }
103
104 /**
105  * Retrieves the last error that occured for the socket.
106  *
107  * @returns An error code.
108  */
109 int Socket::GetError(void) const
110 {
111         int opt;
112         socklen_t optlen = sizeof(opt);
113
114         int rc = getsockopt(GetFD(), SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen);
115
116         if (rc >= 0)
117                 return opt;
118
119         return 0;
120 }
121
122 /**
123  * Formats a sockaddr in a human-readable way.
124  *
125  * @returns A String describing the sockaddr.
126  */
127 String Socket::GetAddressFromSockaddr(sockaddr *address, socklen_t len)
128 {
129         char host[NI_MAXHOST];
130         char service[NI_MAXSERV];
131
132         if (getnameinfo(address, len, host, sizeof(host), service,
133             sizeof(service), NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
134 #ifndef _WIN32
135                 Log(LogCritical, "Socket")
136                     << "getnameinfo() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
137
138                 BOOST_THROW_EXCEPTION(socket_error()
139                     << boost::errinfo_api_function("getnameinfo")
140                     << boost::errinfo_errno(errno));
141 #else /* _WIN32 */
142                 Log(LogCritical, "Socket")
143                     << "getnameinfo() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
144
145                 BOOST_THROW_EXCEPTION(socket_error()
146                     << boost::errinfo_api_function("getnameinfo")
147                     << errinfo_win32_error(WSAGetLastError()));
148 #endif /* _WIN32 */
149         }
150
151         std::ostringstream s;
152         s << "[" << host << "]:" << service;
153         return s.str();
154 }
155
156 /**
157  * Returns a String describing the local address of the socket.
158  *
159  * @returns A String describing the local address.
160  */
161 String Socket::GetClientAddress(void)
162 {
163         boost::mutex::scoped_lock lock(m_SocketMutex);
164
165         sockaddr_storage sin;
166         socklen_t len = sizeof(sin);
167
168         if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) {
169 #ifndef _WIN32
170                 Log(LogCritical, "Socket")
171                     << "getsockname() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
172
173                 BOOST_THROW_EXCEPTION(socket_error()
174                     << boost::errinfo_api_function("getsockname")
175                     << boost::errinfo_errno(errno));
176 #else /* _WIN32 */
177                 Log(LogCritical, "Socket")
178                     << "getsockname() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
179
180                 BOOST_THROW_EXCEPTION(socket_error()
181                     << boost::errinfo_api_function("getsockname")
182                     << errinfo_win32_error(WSAGetLastError()));
183 #endif /* _WIN32 */
184         }
185
186         String address;
187         try {
188                 address = GetAddressFromSockaddr((sockaddr *)&sin, len);
189         } catch (const std::exception&) {
190                 /* already logged */
191         }
192
193         return address;
194 }
195
196 /**
197  * Returns a String describing the peer address of the socket.
198  *
199  * @returns A String describing the peer address.
200  */
201 String Socket::GetPeerAddress(void)
202 {
203         boost::mutex::scoped_lock lock(m_SocketMutex);
204
205         sockaddr_storage sin;
206         socklen_t len = sizeof(sin);
207
208         if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) {
209 #ifndef _WIN32
210                 Log(LogCritical, "Socket")
211                     << "getpeername() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
212
213                 BOOST_THROW_EXCEPTION(socket_error()
214                     << boost::errinfo_api_function("getpeername")
215                     << boost::errinfo_errno(errno));
216 #else /* _WIN32 */
217                 Log(LogCritical, "Socket")
218                     << "getpeername() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
219
220                 BOOST_THROW_EXCEPTION(socket_error()
221                     << boost::errinfo_api_function("getpeername")
222                     << errinfo_win32_error(WSAGetLastError()));
223 #endif /* _WIN32 */
224         }
225
226         String address;
227         try {
228                 address = GetAddressFromSockaddr((sockaddr *)&sin, len);
229         } catch (const std::exception&) {
230                 /* already logged */
231         }
232
233         return address;
234 }
235
236 /**
237  * Starts listening for incoming client connections.
238  */
239 void Socket::Listen(void)
240 {
241         if (listen(GetFD(), SOMAXCONN) < 0) {
242 #ifndef _WIN32
243                 Log(LogCritical, "Socket")
244                     << "listen() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
245
246                 BOOST_THROW_EXCEPTION(socket_error()
247                     << boost::errinfo_api_function("listen")
248                     << boost::errinfo_errno(errno));
249 #else /* _WIN32 */
250                 Log(LogCritical, "Socket")
251                     << "listen() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
252
253                 BOOST_THROW_EXCEPTION(socket_error()
254                     << boost::errinfo_api_function("listen")
255                     << errinfo_win32_error(WSAGetLastError()));
256 #endif /* _WIN32 */
257         }
258 }
259
260 /**
261  * Sends data for the socket.
262  */
263 size_t Socket::Write(const void *buffer, size_t count)
264 {
265         int rc;
266
267 #ifndef _WIN32
268         rc = write(GetFD(), (const char *)buffer, count);
269 #else /* _WIN32 */
270         rc = send(GetFD(), (const char *)buffer, count, 0);
271 #endif /* _WIN32 */
272
273         if (rc < 0) {
274 #ifndef _WIN32
275                 Log(LogCritical, "Socket")
276                     << "send() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
277
278                 BOOST_THROW_EXCEPTION(socket_error()
279                     << boost::errinfo_api_function("send")
280                     << boost::errinfo_errno(errno));
281 #else /* _WIN32 */
282                 Log(LogCritical, "Socket")
283                     << "send() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
284
285                 BOOST_THROW_EXCEPTION(socket_error()
286                     << boost::errinfo_api_function("send")
287                     << errinfo_win32_error(WSAGetLastError()));
288 #endif /* _WIN32 */
289         }
290
291         return rc;
292 }
293
294 /**
295  * Processes data that can be written for this socket.
296  */
297 size_t Socket::Read(void *buffer, size_t count)
298 {
299         int rc;
300
301 #ifndef _WIN32
302         rc = read(GetFD(), (char *)buffer, count);
303 #else /* _WIN32 */
304         rc = recv(GetFD(), (char *)buffer, count, 0);
305 #endif /* _WIN32 */
306
307         if (rc < 0) {
308 #ifndef _WIN32
309                 Log(LogCritical, "Socket")
310                     << "recv() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
311
312                 BOOST_THROW_EXCEPTION(socket_error()
313                     << boost::errinfo_api_function("recv")
314                     << boost::errinfo_errno(errno));
315 #else /* _WIN32 */
316                 Log(LogCritical, "Socket")
317                     << "recv() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
318
319                 BOOST_THROW_EXCEPTION(socket_error()
320                     << boost::errinfo_api_function("recv")
321                     << errinfo_win32_error(WSAGetLastError()));
322 #endif /* _WIN32 */
323         }
324
325         return rc;
326 }
327
328 /**
329  * Accepts a new client and creates a new client object for it.
330  */
331 Socket::Ptr Socket::Accept(void)
332 {
333         int fd;
334         sockaddr_storage addr;
335         socklen_t addrlen = sizeof(addr);
336
337         fd = accept(GetFD(), (sockaddr *)&addr, &addrlen);
338
339         if (fd < 0) {
340 #ifndef _WIN32
341                 Log(LogCritical, "Socket")
342                     << "accept() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
343
344                 BOOST_THROW_EXCEPTION(socket_error()
345                     << boost::errinfo_api_function("accept")
346                     << boost::errinfo_errno(errno));
347 #else /* _WIN32 */
348                 Log(LogCritical, "Socket")
349                     << "accept() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
350
351                 BOOST_THROW_EXCEPTION(socket_error()
352                     << boost::errinfo_api_function("accept")
353                     << errinfo_win32_error(WSAGetLastError()));
354 #endif /* _WIN32 */
355         }
356
357         return new Socket(fd);
358 }
359
360 bool Socket::Poll(bool read, bool write, struct timeval *timeout)
361 {
362         int rc;
363
364 #ifdef _WIN32
365         fd_set readfds, writefds, exceptfds;
366
367         FD_ZERO(&readfds);
368         if (read)
369                 FD_SET(GetFD(), &readfds);
370
371         FD_ZERO(&writefds);
372         if (write)
373                 FD_SET(GetFD(), &writefds);
374
375         FD_ZERO(&exceptfds);
376         FD_SET(GetFD(), &exceptfds);
377
378         rc = select(GetFD() + 1, &readfds, &writefds, &exceptfds, timeout);
379
380         if (rc < 0) {
381                 Log(LogCritical, "Socket")
382                     << "select() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
383
384                 BOOST_THROW_EXCEPTION(socket_error()
385                     << boost::errinfo_api_function("select")
386                     << errinfo_win32_error(WSAGetLastError()));
387         }
388 #else /* _WIN32 */
389         pollfd pfd;
390         pfd.fd = GetFD();
391         pfd.events = (read ? POLLIN : 0) | (write ? POLLOUT : 0);
392         pfd.revents = 0;
393
394         rc = poll(&pfd, 1, timeout ? (timeout->tv_sec + 1000 + timeout->tv_usec / 1000) : -1);
395
396         if (rc < 0) {
397                 Log(LogCritical, "Socket")
398                     << "poll() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
399
400                 BOOST_THROW_EXCEPTION(socket_error()
401                     << boost::errinfo_api_function("poll")
402                     << boost::errinfo_errno(errno));
403         }
404 #endif /* _WIN32 */
405
406         return (rc != 0);
407 }
408
409 void Socket::MakeNonBlocking(void)
410 {
411 #ifdef _WIN32
412         Utility::SetNonBlockingSocket(GetFD());
413 #else /* _WIN32 */
414         Utility::SetNonBlocking(GetFD());
415 #endif /* _WIN32 */
416 }
417
418 void Socket::SocketPair(SOCKET s[2])
419 {
420         if (dumb_socketpair(s, 0) < 0)
421                 BOOST_THROW_EXCEPTION(socket_error()
422                     << boost::errinfo_api_function("socketpair")
423                     << boost::errinfo_errno(errno));
424 }
425