]> granicus.if.org Git - icinga2/blob - base/socket.cpp
dba50bf5224086ded59f70c9a9029eff6fdc5945
[icinga2] / base / socket.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012 Icinga Development Team (http://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 "i2-base.h"
21
22 using namespace icinga;
23
24 /**
25  * A collection of weak pointers to Socket objects which have been
26  * registered with the socket sub-system.
27  */
28 Socket::CollectionType Socket::Sockets;
29
30 /**
31  * Constructor for the Socket class.
32  */
33 Socket::Socket(void)
34 {
35         m_FD = INVALID_SOCKET;
36 }
37
38 /**
39  * Destructor for the Socket class.
40  */
41 Socket::~Socket(void)
42 {
43         CloseInternal(true);
44 }
45
46 /**
47  * Registers the socket and starts handling events for it.
48  */
49 void Socket::Start(void)
50 {
51         assert(m_FD != INVALID_SOCKET);
52
53         OnException += bind_weak(&Socket::ExceptionEventHandler, shared_from_this());
54
55         Sockets.push_back(static_pointer_cast<Socket>(shared_from_this()));
56 }
57
58 /**
59  * Unregisters the sockets and stops handling events for it.
60  */
61 void Socket::Stop(void)
62 {
63         Sockets.remove_if(WeakPtrEqual<Socket>(this));
64 }
65
66 /**
67  * Sets the file descriptor for this socket object.
68  *
69  * @param fd The file descriptor.
70  */
71 void Socket::SetFD(SOCKET fd)
72 {
73         unsigned long lTrue = 1;
74
75         if (fd != INVALID_SOCKET) {
76 #ifdef F_GETFL
77                 int flags;
78                 flags = fcntl(fd, F_GETFL, 0);
79                 if (flags < 0)
80                         throw PosixException("fcntl failed", errno);
81
82                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
83                         throw PosixException("fcntl failed", errno);
84 #else /* F_GETFL */
85                 ioctlsocket(fd, FIONBIO, &lTrue);
86 #endif /* F_GETFL */
87         }
88
89         m_FD = fd;
90 }
91
92 /**
93  * Retrieves the file descriptor for this socket object.
94  *
95  * @returns The file descriptor.
96  */
97 SOCKET Socket::GetFD(void) const
98 {
99         return m_FD;
100 }
101
102 /**
103  * Closes the socket.
104  */
105 void Socket::Close(void)
106 {
107         CloseInternal(false);
108 }
109
110 /**
111  * Closes the socket.
112  *
113  * @param from_dtor Whether this method was called from the destructor.
114  */
115 void Socket::CloseInternal(bool from_dtor)
116 {
117         if (m_FD == INVALID_SOCKET)
118                 return;
119
120         closesocket(m_FD);
121         m_FD = INVALID_SOCKET;
122
123         /* nobody can possibly have a valid event subscription when the
124                 destructor has been called */
125         if (!from_dtor) {
126                 Stop();
127
128                 EventArgs ea;
129                 ea.Source = shared_from_this();
130                 OnClosed(ea);
131         }
132 }
133
134 /**
135  * Retrieves the last error that occured for the socket.
136  *
137  * @returns An error code.
138  */
139 int Socket::GetError(void) const
140 {
141         int opt;
142         socklen_t optlen = sizeof(opt);
143
144         int rc = getsockopt(GetFD(), SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen);
145
146         if (rc >= 0)
147                 return opt;
148
149         return 0;
150 }
151
152 /**
153  * Retrieves the last socket error.
154  *
155  * @returns An error code.
156  */
157 int Socket::GetLastSocketError(void)
158 {
159 #ifdef _WIN32
160         return WSAGetLastError();
161 #else /* _WIN32 */
162         return errno;
163 #endif /* _WIN32 */
164 }
165
166 /**
167  * Handles a socket error by calling the OnError event or throwing an exception
168  * when there are no observers for the OnError event.
169  *
170  * @param ex An exception.
171  */
172 void Socket::HandleSocketError(const std::exception& ex)
173 {
174         if (OnError.HasObservers()) {
175                 SocketErrorEventArgs sea(ex);
176                 OnError(sea);
177
178                 Close();
179         } else {
180                 throw ex;
181         }
182 }
183
184 /**
185  * Processes errors that have occured for the socket.
186  *
187  * @param - Event arguments for the socket error.
188  * @returns 0
189  */
190 int Socket::ExceptionEventHandler(const EventArgs&)
191 {
192         HandleSocketError(SocketException(
193             "select() returned fd in except fdset", GetError()));
194
195         return 0;
196 }
197
198 /**
199  * Checks whether data should be read for this socket object.
200  *
201  * @returns true if the socket should be registered for reading, false otherwise.
202  */
203 bool Socket::WantsToRead(void) const
204 {
205         return false;
206 }
207
208 /**
209  * Checks whether data should be written for this socket object.
210  *
211  * @returns true if the socket should be registered for writing, false otherwise.
212  */
213 bool Socket::WantsToWrite(void) const
214 {
215         return false;
216 }
217
218 /**
219  * Formats a sockaddr in a human-readable way.
220  *
221  * @returns A string describing the sockaddr.
222  */
223 string Socket::GetAddressFromSockaddr(sockaddr *address, socklen_t len)
224 {
225         char host[NI_MAXHOST];
226         char service[NI_MAXSERV];
227
228         if (getnameinfo(address, len, host, sizeof(host), service, sizeof(service), NI_NUMERICHOST | NI_NUMERICSERV) < 0)
229                 throw SocketException("getnameinfo() failed",
230                     GetLastSocketError());
231
232         stringstream s;
233         s << "[" << host << "]:" << service;
234         return s.str();
235 }
236
237 /**
238  * Returns a string describing the local address of the socket.
239  *
240  * @returns A string describing the local address.
241  */
242 string Socket::GetClientAddress(void)
243 {
244         sockaddr_storage sin;
245         socklen_t len = sizeof(sin);
246
247         if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) {
248                 HandleSocketError(SocketException(
249                     "getsockname() failed", GetError()));
250
251                 return string();
252         }
253
254         return GetAddressFromSockaddr((sockaddr *)&sin, len);
255 }
256
257 /**
258  * Returns a string describing the peer address of the socket.
259  *
260  * @returns A string describing the peer address.
261  */
262 string Socket::GetPeerAddress(void)
263 {
264         sockaddr_storage sin;
265         socklen_t len = sizeof(sin);
266
267         if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) {
268                 HandleSocketError(SocketException(
269                     "getpeername() failed", GetError()));
270
271                 return string();
272         }
273
274         return GetAddressFromSockaddr((sockaddr *)&sin, len);
275 }
276
277 /**
278  * Constructor for the SocketException class.
279  *
280  * @param message The error message.
281  * @param errorCode The error code.
282  */
283 SocketException::SocketException(const string& message, int errorCode)  
284 {
285 #ifdef _WIN32
286         string details = Win32Exception::FormatErrorCode(errorCode);
287 #else /* _WIN32 */
288         string details = PosixException::FormatErrorCode(errorCode);
289 #endif /* _WIN32 */
290
291         string msg = message + ": " + details;
292         SetMessage(msg.c_str());
293 }