SocketUtils.cpp

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

Generated on Fri Jun 16 15:39:09 2006 for blocxx by  doxygen 1.4.6