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