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"
41 #include "blocxx/SocketUtils.hpp"
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"
48 #include "blocxx/TimeoutTimer.hpp"
49 
50 #if defined(BLOCXX_WIN32)
51 #include "blocxx/SocketAddress.hpp"
52 #endif
53 
54 #ifndef BLOCXX_HAVE_GETHOSTBYNAME_R
55 #include "blocxx/Mutex.hpp"
56 #include "blocxx/MutexLock.hpp"
57 #endif
58 
59 extern "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 
82 namespace BLOCXX_NAMESPACE
83 {
84 
85 namespace SocketUtils
86 {
87 
89 String
90 inetAddrToString(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)
107 int
108 waitForIO(SocketHandle_t fd, HANDLE eventArg, int timeOutSecs,
109  long networkEvents)
110 {
111  return waitForIO(fd, eventArg, Timeout::relative(timeOutSecs), networkEvents);
112 }
113 
114 int
115 waitForIO(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
199 
200 int
202 {
203  return waitForIO(fd, Timeout::relative(timeOutSecs), waitFlag);
204 }
205 
207 int
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  }
230  Select::SelectObjectArray selarray;
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
282 extern Mutex gethostbynameMutex; // defined in SocketAddress.cpp
283 namespace 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