blocxx
SocketUtils.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2005, Vintela, Inc. All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Vintela, Inc.,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
39#include "blocxx/BLOCXX_config.h"
42#include "blocxx/Assertion.hpp"
43#include "blocxx/Socket.hpp"
44#include "blocxx/Format.hpp"
45#include "blocxx/Thread.hpp"
46#include "blocxx/System.hpp"
47#include "blocxx/Select.hpp"
49
50#if defined(BLOCXX_WIN32)
52#endif
53
54#ifndef BLOCXX_HAVE_GETHOSTBYNAME_R
55#include "blocxx/Mutex.hpp"
56#include "blocxx/MutexLock.hpp"
57#endif
58
59extern "C"
60{
61#if !defined(BLOCXX_WIN32)
63
64#include <ctype.h>
65#include <sys/types.h>
66#include <sys/wait.h>
67#include <sys/time.h>
68#include <sys/socket.h>
69#ifdef BLOCXX_HAVE_SYS_RESOURCE_H
70#include <sys/resource.h>
71#endif
72#include <netdb.h>
73#include <arpa/inet.h>
74#include <unistd.h>
75#endif
76}
77
78#include <cstring>
79#include <cstdio>
80#include <cerrno>
81
82namespace BLOCXX_NAMESPACE
83{
84
85namespace SocketUtils
86{
87
90inetAddrToString(UInt64 addr)
91{
92 sockaddr_in iaddr;
93 iaddr.sin_family = AF_INET;
94 iaddr.sin_addr.s_addr = addr;
95 iaddr.sin_port = 0;
96#ifdef BLOCXX_HAVE_IPV6
97 char buf[INET6_ADDRSTRLEN];
98 String s(inet_ntop(iaddr.sin_family, &(iaddr.sin_addr), buf, sizeof(buf)));
99#else
100 String s(inet_ntoa(iaddr.sin_addr));
101#endif
102
103 return s;
104}
105
106#if defined(BLOCXX_WIN32)
107int
108waitForIO(SocketHandle_t fd, HANDLE eventArg, int timeOutSecs,
109 long networkEvents)
110{
111 return waitForIO(fd, eventArg, Timeout::relative(timeOutSecs), networkEvents);
112}
113
114int
115waitForIO(SocketHandle_t fd, HANDLE eventArg, const Timeout& classTimeout,
116 long networkEvents)
117{
118 TimeoutTimer timer(classTimeout);
119
120 DWORD timeout= timer.asDWORDMs();
121
122 if (networkEvents != -1L)
123 {
124 if(::WSAEventSelect(fd, eventArg, networkEvents) != 0)
125 {
126 BLOCXX_THROW(SocketException,
127 Format("WSAEventSelect failed in waitForIO: %1",
128 System::lastErrorMsg(true)).c_str());
129 }
130 }
131
132 int cc;
133 if(Socket::getShutDownMechanism() != NULL)
134 {
135 HANDLE events[2];
136 events[0] = Socket::getShutDownMechanism();
137 events[1] = eventArg;
138
139 DWORD index = ::WaitForMultipleObjects(
140 2,
141 events,
142 FALSE,
143 timeout);
144
145 switch (index)
146 {
147 case WAIT_FAILED:
148 cc = -1;
149 break;
150 case WAIT_TIMEOUT:
151 cc = ETIMEDOUT;
152 break;
153 default:
154 index -= WAIT_OBJECT_0;
155 // If not shutdown event, then reset
156 if (index != 0)
157 {
158 ::ResetEvent(eventArg);
159 cc = 0;
160 }
161 else
162 {
163 // Shutdown handle was signaled
164 cc = -2;
165 }
166 break;
167 }
168 }
169 else
170 {
171 switch(::WaitForSingleObject(eventArg, timeout))
172 {
173 case WAIT_OBJECT_0:
174 ::ResetEvent(eventArg);
175 cc = 0;
176 break;
177 case WAIT_TIMEOUT:
178 cc = ETIMEDOUT;
179 break;
180 default:
181 cc = -1;
182 break;
183 }
184 }
185
186 // Set socket back to blocking
187 if(::WSAEventSelect(fd, eventArg, 0) != 0)
188 {
189 BLOCXX_THROW(SocketException,
190 Format("Resetting socket with WSAEventSelect failed: %1",
191 System::lastErrorMsg(true)).c_str());
192 }
193 u_long ioctlarg = 0;
194 ::ioctlsocket(fd, FIONBIO, &ioctlarg);
195 return cc;
196}
197
198#else
200int
202{
203 return waitForIO(fd, Timeout::relative(timeOutSecs), waitFlag);
204}
205
207int
209{
210 if (fd == -1)
211 {
212 errno = EBADF;
213 return -1;
214 }
215
216 Select::SelectObject so(fd);
217 if (waitFlag == SocketFlags::E_WAIT_FOR_INPUT)
218 {
219 so.waitForRead = true;
220 }
221 else if (waitFlag == SocketFlags::E_WAIT_FOR_OUTPUT)
222 {
223 so.waitForWrite = true;
224 }
225 else
226 {
227 so.waitForRead = true;
228 so.waitForWrite = true;
229 }
231 selarray.push_back(so);
232
233 PosixUnnamedPipeRef lUPipe;
234 int pipefd = -1;
236 {
238 lUPipe = foo.cast_to<PosixUnnamedPipe>();
239 BLOCXX_ASSERT(lUPipe);
240 pipefd = lUPipe->getInputHandle();
241 }
242 if (pipefd != -1)
243 {
244 so = Select::SelectObject(pipefd);
245 so.waitForRead = true;
246 selarray.push_back(so);
247 }
248
249 int rc = Select::selectRW(selarray, timeout);
250 switch (rc)
251 {
253 rc = ETIMEDOUT;
254 break;
255 case 2:
256 rc = -1; // pipe was signalled
257 errno = ECANCELED;
258 break;
259 case 1:
260 if (pipefd != -1)
261 {
262 if (selarray[1].readAvailable)
263 {
264 rc = -1;
265 }
266 }
267 if (selarray[0].writeAvailable || selarray[0].readAvailable)
268 {
269 rc = 0;
270 }
271 break;
272 default:
273 rc = -1;
274 }
275 return rc;
276
277}
278#endif //
279
280#ifndef BLOCXX_HAVE_GETHOSTBYNAME_R
281} // end namespace SocketUtils
282extern Mutex gethostbynameMutex; // defined in SocketAddress.cpp
283namespace SocketUtils {
284#endif
285
286#ifndef BLOCXX_WIN32
288{
289 char hostName [2048];
290 if (gethostname (hostName, sizeof(hostName)) == 0)
291 {
292#ifndef BLOCXX_HAVE_GETHOSTBYNAME_R
294 struct hostent *he;
295 if ((he = gethostbyname (hostName)) != 0)
296 {
297 return he->h_name;
298 }
299 else
300 {
301 BLOCXX_THROW(SocketException, Format("SocketUtils::getFullyQualifiedHostName: gethostbyname failed: %1", h_errno).c_str());
302 }
303#else
304 hostent hostbuf;
305 hostent* host = &hostbuf;
306#if (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 6 || BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 5)
307 char buf[2048];
308 int h_err = 0;
309#elif (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 3)
310 hostent_data hostdata;
311 int h_err = 0;
312#else
313#error Not yet supported: gethostbyname_r() with other argument counts.
314#endif /* BLOCXX_GETHOSTBYNAME_R_ARGUMENTS */
315 // gethostbyname_r will randomly fail on some platforms/networks
316 // maybe the DNS server is overloaded or something. So we'll
317 // give it a few tries to see if it can get it right.
318 bool worked = false;
319 for (int i = 0; i < 10 && (!worked || host == 0); ++i)
320 {
321#if (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 6)
322 if (gethostbyname_r(hostName, &hostbuf, buf, sizeof(buf),
323 &host, &h_err) != -1)
324 {
325 worked = true;
326 break;
327 }
328#elif (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 5)
329 // returns NULL if not successful
330 if ((host = gethostbyname_r(hostName, &hostbuf, buf, sizeof(buf), &h_err))) {
331 worked = true;
332 break;
333 }
334#elif (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 3)
335 if (gethostbyname_r(hostName, &hostbuf, &hostdata) == 0)
336 {
337 worked = true;
338 break;
339 }
340 else
341 {
342 h_err = h_errno;
343 }
344#else
345#error Not yet supported: gethostbyname_r() with other argument counts.
346#endif /* BLOCXX_GETHOSTBYNAME_R_ARGUMENTS */
347 }
348 if (worked && host != 0)
349 {
350 return host->h_name;
351 }
352 else
353 {
354 BLOCXX_THROW(SocketException, Format("SocketUtils::getFullyQualifiedHostName: gethostbyname_r(%1) failed: %2", hostName, h_err).c_str());
355 }
356#endif
357 }
358 else
359 {
360 BLOCXX_THROW(SocketException, Format("SocketUtils::getFullyQualifiedHostName: gethostname failed: %1(%2)", errno, strerror(errno)).c_str());
361 }
362 return "";
363}
364#else
365// WIN32 defined
367{
368 String rv;
369 struct hostent *hostentp;
370 char bfr[1024], ipaddrstr[128];
371 struct in_addr iaHost;
372
373 if(gethostname(bfr, sizeof(bfr)-1) == SOCKET_ERROR)
374 {
376 Format("SocketUtils::getFullyQualifiedHostName: gethostname failed: %1(%2)",
377 WSAGetLastError(), System::lastErrorMsg(true)).c_str());
378 }
379
380 if(strchr(bfr, '.'))
381 {
382 // Guess we already have the DNS name
383 return String(bfr);
384 }
385
386 if((hostentp = gethostbyname(bfr)) == NULL)
387 {
388 BLOCXX_THROW(SocketException,
389 Format("SocketUtils::getFullyQualifiedHostName: gethostbyname"
390 " failed: %1(%2)", WSAGetLastError(),
391 System::lastErrorMsg(true)).c_str());
392 }
393
394 if(strchr(hostentp->h_name, '.'))
395 {
396 rv = hostentp->h_name;
397 }
398 else
399 {
400 sockaddr_in addr;
401 addr.sin_family = AF_INET;
402 addr.sin_port = 0;
403 memcpy(&addr.sin_addr, hostentp->h_addr_list[0], sizeof(addr.sin_addr));
404#ifdef BLOCXX_HAVE_IPV6
405 char buf[INET6_ADDRSTRLEN];
406 rv = inet_ntop(addr.sin_family, &(addr.sin_addr), buf, sizeof(buf));
407#else
408 rv = inet_ntoa(addr.sin_addr);
409#endif
410
411 iaHost.s_addr = inet_addr(rv.c_str());
412 if(iaHost.s_addr != INADDR_NONE)
413 {
414 hostentp = gethostbyaddr((const char*)&iaHost,
415 sizeof(struct in_addr), AF_INET);
416 if(hostentp)
417 {
418 if(strchr(hostentp->h_name, '.'))
419 {
420 // GOT IT
421 rv = hostentp->h_name;
422 }
423 }
424 }
425 }
426
427 return rv;
428}
429#endif
430
431
432} // end namespace SocketUtils
433
434} // end namespace BLOCXX_NAMESPACE
435
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define ETIMEDOUT
Array<> wraps std::vector<> in COWReference<> adding ref counting and copy on write capability.
Definition ArrayFwd.hpp:46
void push_back(const T &x)
Append an element to the end of the Array.
IntrusiveReference< U > cast_to() const
static ShutDownMechanism_t getShutDownMechanism()
Definition Socket.hpp:296
This String class is an abstract data type that represents as NULL terminated string of characters.
Definition String.hpp:67
const char * c_str() const
Definition String.cpp:905
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition Timeout.hpp:56
static Timeout relative(float seconds)
Definition Timeout.cpp:58
const int SELECT_TIMEOUT
The value returned from select when the timeout value has expired.
Definition Select.hpp:59
int selectRW(SelectObjectArray &selarray, UInt32 ms)
Definition Select.cpp:92
int waitForIO(SocketHandle_t fd, int timeOutSecs, SocketFlags::EWaitDirectionFlag waitFlag)
Wait for input or output on a socket.
String getFullyQualifiedHostName()
Get the fully qualified host name.
String inetAddrToString(UInt64 addr)
String lastErrorMsg(bool socketError)
Definition System.cpp:96
Taken from RFC 1321.
bool waitForRead
Input parameter. Set it to true to indicate that waiting for read availability on s is desired.
Definition Select.hpp:103
bool waitForWrite
Input parameter. Set it to true to indicate that waiting for write availability on s is desired.
Definition Select.hpp:105