Win32SocketBaseImpl.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 
00039 #include "blocxx/BLOCXX_config.h"
00040 
00041 #if defined(BLOCXX_WIN32)
00042 
00043 #include "blocxx/SocketBaseImpl.hpp"
00044 #include "blocxx/SocketUtils.hpp"
00045 #include "blocxx/Format.hpp"
00046 #include "blocxx/Assertion.hpp"
00047 #include "blocxx/IOException.hpp"
00048 #include "blocxx/Mutex.hpp"
00049 #include "blocxx/MutexLock.hpp"
00050 #include "blocxx/PosixUnnamedPipe.hpp"
00051 #include "blocxx/Socket.hpp"
00052 #include "blocxx/Thread.hpp"
00053 #include "blocxx/System.hpp"
00054 
00055 #include <cstdio>
00056 #include <cerrno>
00057 #include <fstream>
00058 #include <ws2tcpip.h>
00059 
00060 namespace
00061 {
00062 
00063 class SockInitializer
00064 {
00065 public:
00066    SockInitializer()
00067    {
00068       WSADATA wsaData;
00069       ::WSAStartup(MAKEWORD(2,2), &wsaData);
00070    }
00071 
00072    ~SockInitializer()
00073    {
00074       ::WSACleanup();
00075    }
00076 };
00077 
00078 // Force Winsock initialization on load
00079 SockInitializer _sockInitializer;
00080 
00082 void
00083 _closeSocket(SOCKET& sockfd)
00084 {
00085    if (sockfd != INVALID_SOCKET)
00086    {
00087       ::closesocket(sockfd);
00088       sockfd = INVALID_SOCKET;
00089    }
00090 }
00091 
00093 int
00094 getAddrFromIface(BLOCXX_NAMESPACE::InetSocketAddress_t& addr)
00095 {
00096    SOCKET sd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00097     if (sd == SOCKET_ERROR)
00098    {
00099       return -1;
00100     }
00101 
00102    int cc = -1;
00103     INTERFACE_INFO interfaceList[20];
00104     unsigned long nBytesReturned;
00105    if (::WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &interfaceList,
00106          sizeof(interfaceList), &nBytesReturned, 0, 0) != SOCKET_ERROR)
00107    {
00108       int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
00109       for (int i = 0; i < nNumInterfaces; ++i)
00110       {
00111          u_long nFlags = interfaceList[i].iiFlags;
00112          if (nFlags & IFF_UP)
00113          {
00114             cc = 0;
00115             ::memcpy(&addr, &(interfaceList[i].iiAddress), sizeof(addr));
00116             if (!(nFlags & IFF_LOOPBACK))
00117             {
00118                break;
00119             }
00120          }
00121       }
00122    }
00123 
00124    ::closesocket(sd);
00125     return 0;
00126 }
00127 
00128 }  // end of unnamed namespace
00129 
00130 namespace BLOCXX_NAMESPACE
00131 {
00132 
00133 using std::istream;
00134 using std::ostream;
00135 using std::iostream;
00136 using std::ifstream;
00137 using std::ofstream;
00138 using std::fstream;
00139 using std::ios;
00140 String SocketBaseImpl::m_traceFileOut;
00141 String SocketBaseImpl::m_traceFileIn;
00142 
00144 // static
00145 int
00146 SocketBaseImpl::waitForEvent(HANDLE eventArg, int secsToTimeout)
00147 {
00148    DWORD timeout = (secsToTimeout != -1)
00149       ? static_cast<DWORD>(secsToTimeout * 1000)
00150       : INFINITE;
00151    
00152    int cc;
00153    if(Socket::getShutDownMechanism() != NULL)
00154    {
00155       HANDLE events[2];
00156       events[0] = Socket::getShutDownMechanism();
00157       events[1] = eventArg;
00158 
00159       DWORD index = ::WaitForMultipleObjects(
00160          2,
00161          events,
00162          FALSE,
00163          timeout);
00164 
00165       switch (index)
00166       {
00167          case WAIT_FAILED:
00168             cc = -2;
00169             break;
00170          case WAIT_TIMEOUT:
00171             cc = -1;
00172             break;
00173          default:
00174             index -= WAIT_OBJECT_0;
00175             // If not shutdown event, then reset
00176             if (index != 0)
00177             {
00178                ::ResetEvent(eventArg);
00179             }
00180             cc = static_cast<int>(index);
00181             break;
00182       }
00183    }
00184    else
00185    {
00186       switch(::WaitForSingleObject(eventArg, timeout))
00187       {
00188          case WAIT_OBJECT_0:
00189             ::ResetEvent(eventArg);
00190             cc = 1;
00191             break;
00192          case WAIT_TIMEOUT:
00193             cc = -1;
00194             break;
00195          default:
00196             cc = -2;
00197             break;
00198       }
00199    }
00200       
00201    return cc;
00202 }
00203 
00204 #pragma warning (push)
00205 #pragma warning (disable: 4355)
00206 
00208 SocketBaseImpl::SocketBaseImpl()
00209    : SelectableIFC()
00210    , IOIFC()
00211    , m_isConnected(false)
00212    , m_sockfd(INVALID_SOCKET)
00213    , m_localAddress()
00214    , m_peerAddress()
00215    , m_event(NULL)
00216    , m_recvTimeoutExprd(false)
00217    , m_streamBuf(this)
00218    , m_in(&m_streamBuf)
00219    , m_out(&m_streamBuf)
00220    , m_inout(&m_streamBuf)
00221    , m_recvTimeout(-1)
00222    , m_sendTimeout(-1)
00223    , m_connectTimeout(0)
00224 {
00225    m_out.exceptions(std::ios::badbit);
00226    m_inout.exceptions(std::ios::badbit);
00227    m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
00228    BLOCXX_ASSERT(m_event != NULL);
00229 }
00231 SocketBaseImpl::SocketBaseImpl(SocketHandle_t fd,
00232       SocketAddress::AddressType addrType)
00233    : SelectableIFC()
00234    , IOIFC()
00235    , m_isConnected(true)
00236    , m_sockfd(fd)
00237    , m_localAddress(SocketAddress::getAnyLocalHost())
00238    , m_peerAddress(SocketAddress::allocEmptyAddress(addrType))
00239    , m_event(NULL)
00240    , m_recvTimeoutExprd(false)
00241    , m_streamBuf(this)
00242    , m_in(&m_streamBuf)
00243    , m_out(&m_streamBuf)
00244    , m_inout(&m_streamBuf)
00245    , m_recvTimeout(-1)
00246    , m_sendTimeout(-1)
00247    , m_connectTimeout(0)
00248 {
00249    BLOCXX_ASSERT(addrType == SocketAddress::INET);
00250 
00251    m_out.exceptions(std::ios::badbit);
00252    m_inout.exceptions(std::ios::badbit);
00253    m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
00254    BLOCXX_ASSERT(m_event != NULL);
00255    fillInetAddrParms();
00256 }
00258 SocketBaseImpl::SocketBaseImpl(const SocketAddress& addr)
00259    : SelectableIFC()
00260    , IOIFC()
00261    , m_isConnected(false)
00262    , m_sockfd(INVALID_SOCKET)
00263    , m_localAddress(SocketAddress::getAnyLocalHost())
00264    , m_peerAddress(addr)
00265    , m_event(NULL)
00266    , m_recvTimeoutExprd(false)
00267    , m_streamBuf(this)
00268    , m_in(&m_streamBuf)
00269    , m_out(&m_streamBuf)
00270    , m_inout(&m_streamBuf)
00271    , m_recvTimeout(-1)
00272    , m_sendTimeout(-1)
00273    , m_connectTimeout(0)
00274 {
00275    m_out.exceptions(std::ios::badbit);
00276    m_inout.exceptions(std::ios::badbit);
00277    m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
00278    BLOCXX_ASSERT(m_event != NULL);
00279    connect(m_peerAddress);
00280 }
00281 
00282 #pragma warning (pop)
00283 
00285 SocketBaseImpl::~SocketBaseImpl()
00286 {
00287    try
00288    {
00289       disconnect();
00290    }
00291    catch (...)
00292    {
00293       // don't let exceptions escape
00294    }
00295    ::CloseHandle(m_event);
00296 }
00298 Select_t
00299 SocketBaseImpl::getSelectObj() const
00300 {
00301    Select_t st;
00302    st.event = m_event;
00303    st.sockfd = m_sockfd;
00304    st.networkevents = FD_READ | FD_WRITE;
00305    st.doreset = true;
00306    return st;
00307 }
00309 void
00310 SocketBaseImpl::connect(const SocketAddress& addr)
00311 {
00312    if (m_isConnected)
00313    {
00314       disconnect();
00315    }
00316    m_streamBuf.reset();
00317    m_in.clear();
00318    m_out.clear();
00319    m_inout.clear();
00320    BLOCXX_ASSERT(addr.getType() == SocketAddress::INET);
00321 
00322    m_sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00323    if (m_sockfd == INVALID_SOCKET)
00324    {
00325       BLOCXX_THROW(SocketException, 
00326          Format("Failed to create a socket: %1",
00327          System::lastErrorMsg(true)).c_str());
00328    }
00329 
00330    int cc;
00331    WSANETWORKEVENTS networkEvents;
00332 
00333    // Connect non-blocking
00334    if(::WSAEventSelect(m_sockfd, m_event, FD_CONNECT) != 0)
00335    {
00336       BLOCXX_THROW(SocketException, 
00337          Format("WSAEventSelect Failed: %1",
00338          System::lastErrorMsg(true)).c_str());
00339    }
00340 
00341    if (::connect(m_sockfd, addr.getNativeForm(), addr.getNativeFormSize())
00342       == SOCKET_ERROR)
00343    {
00344       int lastError = ::WSAGetLastError();
00345         if (lastError != WSAEWOULDBLOCK && lastError != WSAEINPROGRESS)
00346       {
00347          _closeSocket(m_sockfd);
00348          BLOCXX_THROW(SocketException,
00349             Format("Failed to connect to: %1: %2(%3)", addr.toString(),
00350                lastError, System::lastErrorMsg(true)).c_str());
00351       }
00352 
00353       int tmoutval = (m_connectTimeout > 0) ? m_connectTimeout : INFINITE;
00354 
00355       // Wait for connection event to come through
00356       while (true)
00357       {
00358          // Wait for the socket's event to get signaled
00359          if ((cc = waitForEvent(m_event, tmoutval)) < 1)
00360          {
00361             _closeSocket(m_sockfd);
00362             switch (cc)
00363             {
00364                case 0:     // Shutdown event
00365                   BLOCXX_THROW(SocketException,
00366                      "Sockets have been shutdown");
00367                case -1: // Timed out
00368                   BLOCXX_THROW(SocketException,
00369                      Format("Win32SocketBaseImpl connection"
00370                         " timed out. Timeout val = %1",
00371                         m_connectTimeout).c_str());
00372                default: // Error on wait
00373                   BLOCXX_THROW(SocketException, Format("SocketBaseImpl::"
00374                      "connect() wait failed: %1(%2)",
00375                      ::WSAGetLastError(),
00376                      System::lastErrorMsg(true)).c_str());
00377             }
00378          }
00379 
00380          // Find out what network event took place
00381          if (::WSAEnumNetworkEvents(m_sockfd, m_event, &networkEvents)
00382             == SOCKET_ERROR)
00383          {
00384             _closeSocket(m_sockfd);
00385             BLOCXX_THROW(SocketException,
00386                Format("SocketBaseImpl::connect()"
00387                   " failed getting network events: %1(%2)",
00388                   ::WSAGetLastError(),
00389                   System::lastErrorMsg(true)).c_str());
00390          }
00391 
00392          // Was it a connect event?
00393          if (networkEvents.lNetworkEvents & FD_CONNECT)
00394          {
00395             // Did connect fail?
00396             if (networkEvents.iErrorCode[FD_CONNECT_BIT])
00397             {
00398                ::WSASetLastError(networkEvents.iErrorCode[FD_CONNECT_BIT]);
00399                _closeSocket(m_sockfd);
00400                     BLOCXX_THROW(SocketException,
00401                   Format("SocketBaseImpl::connect() failed: %1(%2)",
00402                   ::WSAGetLastError(),
00403                   System::lastErrorMsg(true)).c_str());
00404             }
00405             break;
00406          }
00407       }  // while (true) - waiting for connection event
00408    }  // if SOCKET_ERROR on connect
00409 
00410    // Set socket back to blocking
00411    if(::WSAEventSelect(m_sockfd, m_event, 0) != 0)
00412    {
00413       _closeSocket(m_sockfd);
00414       BLOCXX_THROW(SocketException, 
00415          Format("Resetting socket with WSAEventSelect Failed: %1",
00416          System::lastErrorMsg(true)).c_str());
00417    }
00418    u_long ioctlarg = 0;
00419    ::ioctlsocket(m_sockfd, FIONBIO, &ioctlarg);
00420 
00421    m_isConnected = true;
00422 
00423 
00424    BLOCXX_ASSERT(addr.getType() == SocketAddress::INET);
00425 
00426    m_peerAddress = addr; 
00427    fillInetAddrParms();
00428 }
00429 
00431 void
00432 SocketBaseImpl::disconnect()
00433 {
00434    if(m_in)
00435    {
00436       m_in.clear(ios::eofbit);
00437    }
00438    if(m_out)
00439    {
00440         m_out.clear(ios::eofbit);
00441    }
00442    if(!m_inout.fail())
00443    {
00444       m_inout.clear(ios::eofbit);
00445    }
00446 
00447    ::SetEvent(m_event);
00448    _closeSocket(m_sockfd);
00449    m_isConnected = false;
00450 }
00451 
00453 void
00454 SocketBaseImpl::fillInetAddrParms()
00455 {
00456    socklen_t len;
00457    InetSocketAddress_t addr;
00458    ::memset(&addr, 0, sizeof(addr));
00459    len = sizeof(addr);
00460    bool gotAddr = false;
00461 
00462    if (m_sockfd != INVALID_SOCKET)
00463    {
00464       len = sizeof(addr);
00465       if (::getsockname(m_sockfd,
00466          reinterpret_cast<struct sockaddr*>(&addr), &len) != SOCKET_ERROR)
00467       {
00468          m_localAddress.assignFromNativeForm(&addr, len);
00469       }
00470       else if (getAddrFromIface(addr) == 0)
00471       {
00472          len = sizeof(addr);
00473          m_localAddress.assignFromNativeForm(&addr, len);
00474       }
00475 
00476       len = sizeof(addr);
00477       if (::getpeername(m_sockfd, reinterpret_cast<struct sockaddr*>(&addr),
00478          &len) != SOCKET_ERROR)
00479       {
00480          m_peerAddress.assignFromNativeForm(&addr, len);
00481       }
00482    }
00483    else if (getAddrFromIface(addr) == 0)
00484    {
00485       m_localAddress.assignFromNativeForm(&addr, len);
00486    }
00487 }
00488 
00489 static Mutex guard;
00491 int
00492 SocketBaseImpl::write(const void* dataOut, int dataOutLen, bool errorAsException)
00493 {
00494    int rc = 0;
00495    bool isError = false;
00496    if (m_isConnected)
00497    {
00498       isError = waitForOutput(m_sendTimeout);
00499       if (isError)
00500       {
00501          rc = -1;
00502       }
00503       else
00504       {
00505          rc = writeAux(dataOut, dataOutLen);
00506          if (!m_traceFileOut.empty() && rc > 0)
00507          {
00508             MutexLock ml(guard);
00509             ofstream traceFile(m_traceFileOut.c_str(), std::ios::app);
00510             if (!traceFile)
00511             {
00512                BLOCXX_THROW(IOException, "Failed opening socket dump file");
00513             }
00514             if (!traceFile.write(static_cast<const char*>(dataOut), rc))
00515             {
00516                BLOCXX_THROW(IOException, "Failed writing to socket dump");
00517             }
00518 
00519             ofstream comboTraceFile(String(m_traceFileOut + "Combo").c_str(), std::ios::app);
00520             if (!comboTraceFile)
00521             {
00522                BLOCXX_THROW(IOException, "Failed opening socket dump file");
00523             }
00524             comboTraceFile << "\n--->Out " << rc << " bytes<---\n";
00525             if (!comboTraceFile.write(static_cast<const char*>(dataOut), rc))
00526             {
00527                BLOCXX_THROW(IOException, "Failed writing to socket dump");
00528             }
00529          }
00530       }
00531    }
00532    else
00533    {
00534       rc = -1;
00535    }
00536    if (rc < 0 && errorAsException)
00537    {
00538       BLOCXX_THROW(SocketException, "SocketBaseImpl::write");
00539    }
00540    return rc;
00541 }
00543 int
00544 SocketBaseImpl::read(void* dataIn, int dataInLen, bool errorAsException)   
00545 {
00546    int rc = 0;
00547    bool isError = false;
00548    if (m_isConnected)
00549    {
00550       isError = waitForInput(m_recvTimeout);
00551       if (isError)
00552       {
00553          rc = -1;
00554       }
00555       else
00556       {
00557          rc = readAux(dataIn, dataInLen);
00558          if (!m_traceFileIn.empty() && rc > 0)
00559          {
00560             MutexLock ml(guard);
00561             ofstream traceFile(m_traceFileIn.c_str(), std::ios::app);
00562             if (!traceFile)
00563             {
00564                BLOCXX_THROW(IOException, "Failed opening tracefile");
00565             }
00566             if (!traceFile.write(reinterpret_cast<const char*>(dataIn), rc))
00567             {
00568                BLOCXX_THROW(IOException, "Failed writing to socket dump");
00569             }
00570 
00571             ofstream comboTraceFile(String(m_traceFileOut + "Combo").c_str(), std::ios::app);
00572             if (!comboTraceFile)
00573             {
00574                BLOCXX_THROW(IOException, "Failed opening socket dump file");
00575             }
00576             comboTraceFile << "\n--->In " << rc << " bytes<---\n";
00577             if (!comboTraceFile.write(reinterpret_cast<const char*>(dataIn), rc))
00578             {
00579                BLOCXX_THROW(IOException, "Failed writing to socket dump");
00580             }
00581          }
00582       }
00583    }
00584    else
00585    {
00586       rc = -1;
00587    }
00588    if (rc < 0)
00589    {
00590       if (errorAsException)
00591          BLOCXX_THROW(SocketException, "SocketBaseImpl::read");
00592    }
00593    return rc;
00594 }
00596 bool
00597 SocketBaseImpl::waitForInput(int timeOutSecs)
00598 {
00599    int rval = SocketUtils::waitForIO(m_sockfd, m_event, timeOutSecs, FD_READ);
00600    if (rval == ETIMEDOUT)
00601    {
00602       m_recvTimeoutExprd = true;
00603    }
00604    else
00605    {
00606       m_recvTimeoutExprd = false;
00607    }
00608    return (rval != 0);
00609 }
00611 bool
00612 SocketBaseImpl::waitForOutput(int timeOutSecs)
00613 {
00614    return SocketUtils::waitForIO(m_sockfd, m_event, timeOutSecs,
00615       FD_WRITE) != 0;
00616 }
00618 istream&
00619 SocketBaseImpl::getInputStream()
00620 {
00621    return m_in;
00622 }
00624 ostream&
00625 SocketBaseImpl::getOutputStream()
00626 {
00627    return m_out;
00628 }
00630 iostream&
00631 SocketBaseImpl::getIOStream()
00632 {
00633    return m_inout;
00634 }
00636 // STATIC
00637 void
00638 SocketBaseImpl::setDumpFiles(const String& in, const String& out)
00639 {
00640    m_traceFileOut = out;
00641    m_traceFileIn = in;
00642 }
00643 
00644 } // end namespace BLOCXX_NAMESPACE
00645 
00646 #endif   // #if defined(BLOCXX_WIN32)

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