blocxx

SocketUtils.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Vintela, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00039 #include "blocxx/BLOCXX_config.h"
00040 #include "blocxx/SocketException.hpp"
00041 #include "blocxx/SocketUtils.hpp"
00042 #include "blocxx/Assertion.hpp"
00043 #include "blocxx/Socket.hpp"
00044 #include "blocxx/Format.hpp"
00045 #include "blocxx/Thread.hpp"
00046 #include "blocxx/System.hpp"
00047 #include "blocxx/Select.hpp"
00048 #include "blocxx/TimeoutTimer.hpp"
00049 
00050 #if defined(BLOCXX_WIN32)
00051 #include "blocxx/SocketAddress.hpp"
00052 #endif
00053 
00054 #ifndef BLOCXX_HAVE_GETHOSTBYNAME_R
00055 #include "blocxx/Mutex.hpp"
00056 #include "blocxx/MutexLock.hpp"
00057 #endif
00058 
00059 extern "C"
00060 {
00061 #if !defined(BLOCXX_WIN32)
00062 #include "blocxx/PosixUnnamedPipe.hpp"
00063 
00064 #include <ctype.h>
00065 #include <sys/types.h>
00066 #include <sys/wait.h>
00067 #include <sys/time.h>
00068 #include <sys/socket.h>
00069 #ifdef BLOCXX_HAVE_SYS_RESOURCE_H
00070 #include <sys/resource.h>
00071 #endif
00072 #include <netdb.h>
00073 #include <arpa/inet.h>
00074 #include <unistd.h>
00075 #endif
00076 }
00077 
00078 #include <cstring>
00079 #include <cstdio>
00080 #include <cerrno>
00081 
00082 namespace BLOCXX_NAMESPACE
00083 {
00084 
00085 namespace SocketUtils 
00086 {
00087 
00089 String
00090 inetAddrToString(UInt64 addr)
00091 {
00092    sockaddr_in iaddr;
00093    iaddr.sin_family = AF_INET;
00094    iaddr.sin_addr.s_addr = addr;
00095    iaddr.sin_port = 0;
00096 #ifdef BLOCXX_HAVE_IPV6
00097    char buf[INET6_ADDRSTRLEN];
00098    String s(inet_ntop(iaddr.sin_family, &(iaddr.sin_addr), buf, sizeof(buf)));
00099 #else
00100    String s(inet_ntoa(iaddr.sin_addr));
00101 #endif
00102 
00103    return s;
00104 }
00105 
00106 #if defined(BLOCXX_WIN32)
00107 int
00108 waitForIO(SocketHandle_t fd, HANDLE eventArg, int timeOutSecs,
00109    long networkEvents)
00110 {
00111    return waitForIO(fd, eventArg, Timeout::relative(timeOutSecs), networkEvents);
00112 }
00113 
00114 int
00115 waitForIO(SocketHandle_t fd, HANDLE eventArg, const Timeout& classTimeout,
00116      long networkEvents)
00117 {
00118    TimeoutTimer timer(classTimeout);
00119 
00120    DWORD timeout= timer.asDWORDMs();
00121 
00122    if (networkEvents != -1L)
00123    {
00124       if(::WSAEventSelect(fd, eventArg, networkEvents) != 0)
00125       {
00126          BLOCXX_THROW(SocketException, 
00127             Format("WSAEventSelect failed in waitForIO: %1",
00128             System::lastErrorMsg(true)).c_str());
00129       }
00130    }
00131 
00132    int cc;
00133    if(Socket::getShutDownMechanism() != NULL)
00134    {
00135       HANDLE events[2];
00136       events[0] = Socket::getShutDownMechanism();
00137       events[1] = eventArg;
00138 
00139       DWORD index = ::WaitForMultipleObjects(
00140          2,
00141          events,
00142          FALSE,
00143          timeout);
00144 
00145       switch (index)
00146       {
00147          case WAIT_FAILED:
00148             cc = -1;
00149             break;
00150          case WAIT_TIMEOUT:
00151             cc = ETIMEDOUT;
00152             break;
00153          default:
00154             index -= WAIT_OBJECT_0;
00155             // If not shutdown event, then reset
00156             if (index != 0)
00157             {
00158                ::ResetEvent(eventArg);
00159                cc = 0;
00160             }
00161             else
00162             {
00163                // Shutdown handle was signaled
00164                cc = -2;
00165             }
00166             break;
00167       }
00168    }
00169    else
00170    {
00171       switch(::WaitForSingleObject(eventArg, timeout))
00172       {
00173          case WAIT_OBJECT_0:
00174             ::ResetEvent(eventArg);
00175             cc = 0;
00176             break;
00177          case WAIT_TIMEOUT:
00178             cc = ETIMEDOUT;
00179             break;
00180          default:
00181             cc = -1;
00182             break;
00183       }
00184    }
00185 
00186    // Set socket back to blocking
00187    if(::WSAEventSelect(fd, eventArg, 0) != 0)
00188    {
00189       BLOCXX_THROW(SocketException, 
00190          Format("Resetting socket with WSAEventSelect failed: %1",
00191          System::lastErrorMsg(true)).c_str());
00192    }
00193    u_long ioctlarg = 0;
00194    ::ioctlsocket(fd, FIONBIO, &ioctlarg);
00195    return cc;
00196 }
00197 
00198 #else
00199 
00200 int
00201 waitForIO(SocketHandle_t fd, int timeOutSecs, SocketFlags::EWaitDirectionFlag waitFlag)
00202 {
00203    return waitForIO(fd, Timeout::relative(timeOutSecs), waitFlag);
00204 }
00205 
00207 int
00208 waitForIO(SocketHandle_t fd, const Timeout& timeout, SocketFlags::EWaitDirectionFlag waitFlag)
00209 {
00210    if (fd == -1)
00211    {
00212       errno = EBADF;
00213       return -1;
00214    }
00215 
00216    Select::SelectObject so(fd); 
00217    if (waitFlag == SocketFlags::E_WAIT_FOR_INPUT)
00218    {
00219       so.waitForRead = true; 
00220    }
00221    else if (waitFlag == SocketFlags::E_WAIT_FOR_OUTPUT)
00222    {
00223       so.waitForWrite = true; 
00224    }
00225    else
00226    {
00227       so.waitForRead = true; 
00228       so.waitForWrite = true; 
00229    }
00230    Select::SelectObjectArray selarray; 
00231    selarray.push_back(so); 
00232 
00233    PosixUnnamedPipeRef lUPipe;
00234    int pipefd = -1;
00235    if (Socket::getShutDownMechanism())
00236    {
00237       UnnamedPipeRef foo = Socket::getShutDownMechanism();
00238       lUPipe = foo.cast_to<PosixUnnamedPipe>();
00239       BLOCXX_ASSERT(lUPipe);
00240       pipefd = lUPipe->getInputHandle();
00241    }
00242    if (pipefd != -1)
00243    {
00244       so = Select::SelectObject(pipefd); 
00245       so.waitForRead = true; 
00246       selarray.push_back(so); 
00247    }
00248 
00249    int rc = Select::selectRW(selarray, timeout); 
00250    switch (rc)
00251    {
00252    case Select::SELECT_TIMEOUT:
00253       rc = ETIMEDOUT; 
00254       break; 
00255    case 2:
00256       rc = -1; // pipe was signalled
00257       errno = ECANCELED;
00258       break; 
00259    case 1: 
00260       if (pipefd != -1)
00261       {
00262          if (selarray[1].readAvailable)
00263          {
00264             rc = -1; 
00265          }
00266       }
00267       if (selarray[0].writeAvailable || selarray[0].readAvailable)
00268       {
00269          rc = 0; 
00270       }
00271       break; 
00272    default: 
00273       rc = -1; 
00274    }
00275    return rc; 
00276 
00277 }
00278 #endif   // 
00279 
00280 #ifndef BLOCXX_HAVE_GETHOSTBYNAME_R
00281 } // end namespace SocketUtils
00282 extern Mutex gethostbynameMutex;  // defined in SocketAddress.cpp
00283 namespace SocketUtils {
00284 #endif
00285 
00286 #ifndef BLOCXX_WIN32
00287 String getFullyQualifiedHostName()
00288 {
00289    char hostName [2048];
00290    if (gethostname (hostName, sizeof(hostName)) == 0)
00291    {
00292 #ifndef BLOCXX_HAVE_GETHOSTBYNAME_R
00293       MutexLock lock(gethostbynameMutex);
00294       struct hostent *he;
00295       if ((he = gethostbyname (hostName)) != 0)
00296       {
00297          return he->h_name;
00298       }
00299       else
00300       {
00301          BLOCXX_THROW(SocketException, Format("SocketUtils::getFullyQualifiedHostName: gethostbyname failed: %1", h_errno).c_str());
00302       }
00303 #else
00304       hostent hostbuf;
00305       hostent* host = &hostbuf;
00306 #if (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 6 || BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 5)
00307       char buf[2048];
00308       int h_err = 0;
00309 #elif (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 3)
00310       hostent_data hostdata;
00311       int h_err = 0;    
00312 #else
00313 #error Not yet supported: gethostbyname_r() with other argument counts.
00314 #endif /* BLOCXX_GETHOSTBYNAME_R_ARGUMENTS */
00315       // gethostbyname_r will randomly fail on some platforms/networks
00316       // maybe the DNS server is overloaded or something.  So we'll
00317       // give it a few tries to see if it can get it right.
00318       bool worked = false;
00319       for (int i = 0; i < 10 && (!worked || host == 0); ++i)
00320       {
00321 #if (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 6)       
00322          if (gethostbyname_r(hostName, &hostbuf, buf, sizeof(buf),
00323                   &host, &h_err) != -1)
00324          {
00325             worked = true;
00326             break;
00327          }
00328 #elif (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 5)
00329          // returns NULL if not successful
00330          if ((host = gethostbyname_r(hostName, &hostbuf, buf, sizeof(buf), &h_err))) {
00331             worked = true;
00332             break;
00333          }
00334 #elif (BLOCXX_GETHOSTBYNAME_R_ARGUMENTS == 3)
00335          if (gethostbyname_r(hostName, &hostbuf, &hostdata) == 0)
00336          {
00337             worked = true;
00338             break;
00339          }
00340          else
00341          {
00342            h_err = h_errno;
00343          }
00344 #else
00345 #error Not yet supported: gethostbyname_r() with other argument counts.
00346 #endif /* BLOCXX_GETHOSTBYNAME_R_ARGUMENTS */
00347       }
00348       if (worked && host != 0)
00349       {
00350          return host->h_name;
00351       }
00352       else
00353       {
00354          BLOCXX_THROW(SocketException, Format("SocketUtils::getFullyQualifiedHostName: gethostbyname_r(%1) failed: %2", hostName, h_err).c_str());
00355       }
00356 #endif
00357    }
00358    else
00359    {
00360       BLOCXX_THROW(SocketException, Format("SocketUtils::getFullyQualifiedHostName: gethostname failed: %1(%2)", errno, strerror(errno)).c_str());
00361    }
00362    return "";
00363 }
00364 #else
00365 // WIN32 defined
00366 String getFullyQualifiedHostName()
00367 {
00368    String rv;
00369    struct hostent *hostentp;
00370    char bfr[1024], ipaddrstr[128];
00371    struct in_addr iaHost;
00372 
00373    if(gethostname(bfr, sizeof(bfr)-1) == SOCKET_ERROR)
00374    {
00375       BLOCXX_THROW(SocketException, 
00376          Format("SocketUtils::getFullyQualifiedHostName: gethostname failed: %1(%2)", 
00377             WSAGetLastError(), System::lastErrorMsg(true)).c_str());
00378    }
00379 
00380    if(strchr(bfr, '.'))
00381    {
00382       // Guess we already have the DNS name
00383       return String(bfr);
00384    }
00385 
00386    if((hostentp = gethostbyname(bfr)) == NULL)
00387    {
00388       BLOCXX_THROW(SocketException, 
00389          Format("SocketUtils::getFullyQualifiedHostName: gethostbyname"
00390             " failed: %1(%2)", WSAGetLastError(),
00391             System::lastErrorMsg(true)).c_str());
00392    }
00393 
00394    if(strchr(hostentp->h_name, '.'))
00395    {
00396       rv = hostentp->h_name;
00397    }
00398    else
00399    {
00400       sockaddr_in     addr;
00401       addr.sin_family = AF_INET;
00402       addr.sin_port   = 0;
00403       memcpy(&addr.sin_addr, hostentp->h_addr_list[0], sizeof(addr.sin_addr));
00404 #ifdef BLOCXX_HAVE_IPV6
00405       char buf[INET6_ADDRSTRLEN];
00406       rv = inet_ntop(addr.sin_family, &(addr.sin_addr), buf, sizeof(buf));
00407 #else
00408       rv = inet_ntoa(addr.sin_addr);
00409 #endif
00410 
00411       iaHost.s_addr = inet_addr(rv.c_str());
00412       if(iaHost.s_addr != INADDR_NONE)
00413       {
00414          hostentp = gethostbyaddr((const char*)&iaHost,
00415             sizeof(struct in_addr), AF_INET);
00416          if(hostentp)
00417          {
00418             if(strchr(hostentp->h_name, '.'))
00419             {
00420                // GOT IT
00421                rv = hostentp->h_name;
00422             }
00423          }
00424       }
00425    }
00426 
00427    return rv;
00428 }
00429 #endif
00430 
00431 
00432 } // end namespace SocketUtils
00433 
00434 } // end namespace BLOCXX_NAMESPACE
00435