blocxx
Win32SocketBaseImpl.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 
40 #include "blocxx/BLOCXX_config.h"
41 
42 #if defined(BLOCXX_WIN32)
43 
45 #include "blocxx/SocketUtils.hpp"
46 #include "blocxx/Format.hpp"
47 #include "blocxx/Assertion.hpp"
48 #include "blocxx/IOException.hpp"
49 #include "blocxx/Mutex.hpp"
50 #include "blocxx/MutexLock.hpp"
51 #include "blocxx/Socket.hpp"
52 #include "blocxx/Thread.hpp"
53 #include "blocxx/System.hpp"
54 #include "blocxx/TimeoutTimer.hpp"
55 
56 #include <cstdio>
57 #include <cerrno>
58 #include <fstream>
59 #include <ws2tcpip.h>
60 
61 namespace
62 {
63 
64 class SockInitializer
65 {
66 public:
67  SockInitializer()
68  {
69  WSADATA wsaData;
70  ::WSAStartup(MAKEWORD(2,2), &wsaData);
71  }
72 
73  ~SockInitializer()
74  {
75  ::WSACleanup();
76  }
77 };
78 
79 // Force Winsock initialization on load
80 SockInitializer _sockInitializer;
81 
83 void
84 _closeSocket(SOCKET& sockfd)
85 {
86  if (sockfd != INVALID_SOCKET)
87  {
88  ::closesocket(sockfd);
89  sockfd = INVALID_SOCKET;
90  }
91 }
92 
94 int
95 getAddrFromIface(BLOCXX_NAMESPACE::InetSocketAddress_t& addr)
96 {
97  SOCKET sd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
98  if (sd == SOCKET_ERROR)
99  {
100  return -1;
101  }
102 
103  int cc = -1;
104  INTERFACE_INFO interfaceList[20];
105  unsigned long nBytesReturned;
106  if (::WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &interfaceList,
107  sizeof(interfaceList), &nBytesReturned, 0, 0) != SOCKET_ERROR)
108  {
109  int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
110  for (int i = 0; i < nNumInterfaces; ++i)
111  {
112  u_long nFlags = interfaceList[i].iiFlags;
113  if (nFlags & IFF_UP)
114  {
115  cc = 0;
116  ::memcpy(&addr, &(interfaceList[i].iiAddress), sizeof(addr));
117  if (!(nFlags & IFF_LOOPBACK))
118  {
119  break;
120  }
121  }
122  }
123  }
124 
125  ::closesocket(sd);
126  return 0;
127 }
128 
129 } // end of unnamed namespace
130 
131 namespace BLOCXX_NAMESPACE
132 {
133 
134 using std::istream;
135 using std::ostream;
136 using std::iostream;
137 using std::ifstream;
138 using std::ofstream;
139 using std::fstream;
140 using std::ios;
143 
145 // static
146 int
147 SocketBaseImpl::waitForEvent(HANDLE eventArg, int secsToTimeout)
148 {
149  DWORD timeout = (secsToTimeout != -1)
150  ? static_cast<DWORD>(secsToTimeout * 1000)
151  : INFINITE;
152 
153  int cc;
154  if(Socket::getShutDownMechanism() != NULL)
155  {
156  HANDLE events[2];
157  events[0] = Socket::getShutDownMechanism();
158  events[1] = eventArg;
159 
160  DWORD index = ::WaitForMultipleObjects(
161  2,
162  events,
163  FALSE,
164  timeout);
165 
166  switch (index)
167  {
168  case WAIT_FAILED:
169  cc = -2;
170  break;
171  case WAIT_TIMEOUT:
172  cc = -1;
173  break;
174  default:
175  index -= WAIT_OBJECT_0;
176  // If not shutdown event, then reset
177  if (index != 0)
178  {
179  ::ResetEvent(eventArg);
180  }
181  cc = static_cast<int>(index);
182  break;
183  }
184  }
185  else
186  {
187  switch(::WaitForSingleObject(eventArg, timeout))
188  {
189  case WAIT_OBJECT_0:
190  ::ResetEvent(eventArg);
191  cc = 1;
192  break;
193  case WAIT_TIMEOUT:
194  cc = -1;
195  break;
196  default:
197  cc = -2;
198  break;
199  }
200  }
201 
202  return cc;
203 }
204 
205 #pragma warning (push)
206 #pragma warning (disable: 4355)
207 
210  : SelectableIFC()
211  , IOIFC()
212  , m_isConnected(false)
213  , m_sockfd(INVALID_SOCKET)
214  , m_localAddress()
215  , m_peerAddress()
216  , m_event(NULL)
217  , m_recvTimeoutExprd(false)
218  , m_streamBuf(this)
219  , m_in(&m_streamBuf)
220  , m_out(&m_streamBuf)
221  , m_inout(&m_streamBuf)
222  , m_recvTimeout(Timeout::infinite)
223  , m_sendTimeout(Timeout::infinite)
224  , m_connectTimeout(Timeout::relative(0))
225 {
226  m_out.exceptions(std::ios::badbit);
227  m_inout.exceptions(std::ios::badbit);
228  m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
229  BLOCXX_ASSERT(m_event != NULL);
230 }
234  : SelectableIFC()
235  , IOIFC()
236  , m_isConnected(true)
237  , m_sockfd(fd)
238  , m_localAddress(SocketAddress::getAnyLocalHost())
239  , m_peerAddress(SocketAddress::allocEmptyAddress(addrType))
240  , m_event(NULL)
241  , m_recvTimeoutExprd(false)
242  , m_streamBuf(this)
243  , m_in(&m_streamBuf)
244  , m_out(&m_streamBuf)
245  , m_inout(&m_streamBuf)
246  , m_recvTimeout(Timeout::infinite)
247  , m_sendTimeout(Timeout::infinite)
248  , m_connectTimeout(Timeout::relative(0))
249 {
250  BLOCXX_ASSERT(addrType == SocketAddress::INET);
251 
252  m_out.exceptions(std::ios::badbit);
253  m_inout.exceptions(std::ios::badbit);
254  m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
255  BLOCXX_ASSERT(m_event != NULL);
256  fillInetAddrParms();
257 }
259 SocketBaseImpl::SocketBaseImpl(const SocketAddress& addr)
260  : SelectableIFC()
261  , IOIFC()
262  , m_isConnected(false)
263  , m_sockfd(INVALID_SOCKET)
264  , m_localAddress(SocketAddress::getAnyLocalHost())
265  , m_peerAddress(addr)
266  , m_event(NULL)
267  , m_recvTimeoutExprd(false)
268  , m_streamBuf(this)
269  , m_in(&m_streamBuf)
270  , m_out(&m_streamBuf)
271  , m_inout(&m_streamBuf)
272  , m_recvTimeout(Timeout::infinite)
273  , m_sendTimeout(Timeout::infinite)
274  , m_connectTimeout(Timeout::relative(0))
275 {
276  m_out.exceptions(std::ios::badbit);
277  m_inout.exceptions(std::ios::badbit);
278  m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
279  BLOCXX_ASSERT(m_event != NULL);
280  connect(m_peerAddress);
281 }
282 
283 #pragma warning (pop)
284 
287 {
288  try
289  {
290  disconnect();
291  }
292  catch (...)
293  {
294  // don't let exceptions escape
295  }
296  ::CloseHandle(m_event);
297 }
299 Select_t
301 {
302  Select_t st;
303  st.event = m_event;
304  st.sockfd = m_sockfd;
305  st.isSocket = true;
306  st.networkevents = FD_READ | FD_WRITE;
307  st.doreset = true;
308  return st;
309 }
311 void
312 SocketBaseImpl::connect(const SocketAddress& addr)
313 {
314  if (m_isConnected)
315  {
316  disconnect();
317  }
318  m_streamBuf.reset();
319  m_in.clear();
320  m_out.clear();
321  m_inout.clear();
322  BLOCXX_ASSERT(addr.getType() == SocketAddress::INET);
323 
324  m_sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
325  if (m_sockfd == INVALID_SOCKET)
326  {
327  BLOCXX_THROW(SocketException,
328  Format("Failed to create a socket: %1",
329  System::lastErrorMsg(true)).c_str());
330  }
331 
332  int cc;
333  WSANETWORKEVENTS networkEvents;
334 
335  // Connect non-blocking
336  if(::WSAEventSelect(m_sockfd, m_event, FD_CONNECT) != 0)
337  {
338  BLOCXX_THROW(SocketException,
339  Format("WSAEventSelect Failed: %1",
340  System::lastErrorMsg(true)).c_str());
341  }
342 
343  if (::connect(m_sockfd, addr.getNativeForm(), addr.getNativeFormSize())
344  == SOCKET_ERROR)
345  {
346  int lastError = ::WSAGetLastError();
347  if (lastError != WSAEWOULDBLOCK && lastError != WSAEINPROGRESS)
348  {
349  _closeSocket(m_sockfd);
350  BLOCXX_THROW(SocketException,
351  Format("Failed to connect to: %1: %2(%3)", addr.toString(),
352  lastError, System::lastErrorMsg(true)).c_str());
353  }
354 
355  TimeoutTimer timer(m_connectTimeout);
356  int tmoutval = timer.asDWORDMs();
357 
358  // Wait for connection event to come through
359  while (true)
360  {
361  // Wait for the socket's event to get signaled
362  if ((cc = waitForEvent(m_event, tmoutval)) < 1)
363  {
364  _closeSocket(m_sockfd);
365  switch (cc)
366  {
367  case 0: // Shutdown event
368  BLOCXX_THROW(SocketException,
369  "Sockets have been shutdown");
370  case -1: // Timed out
371  BLOCXX_THROW(SocketException,
372  Format("Win32SocketBaseImpl connection"
373  " timed out. Timeout val = %1",
374  tmoutval).c_str());
375  default: // Error on wait
376  BLOCXX_THROW(SocketException, Format("SocketBaseImpl::"
377  "connect() wait failed: %1(%2)",
378  ::WSAGetLastError(),
379  System::lastErrorMsg(true)).c_str());
380  }
381  }
382 
383  // Find out what network event took place
384  if (::WSAEnumNetworkEvents(m_sockfd, m_event, &networkEvents)
385  == SOCKET_ERROR)
386  {
387  _closeSocket(m_sockfd);
388  BLOCXX_THROW(SocketException,
389  Format("SocketBaseImpl::connect()"
390  " failed getting network events: %1(%2)",
391  ::WSAGetLastError(),
392  System::lastErrorMsg(true)).c_str());
393  }
394 
395  // Was it a connect event?
396  if (networkEvents.lNetworkEvents & FD_CONNECT)
397  {
398  // Did connect fail?
399  if (networkEvents.iErrorCode[FD_CONNECT_BIT])
400  {
401  ::WSASetLastError(networkEvents.iErrorCode[FD_CONNECT_BIT]);
402  _closeSocket(m_sockfd);
403  BLOCXX_THROW(SocketException,
404  Format("SocketBaseImpl::connect() failed: %1(%2)",
405  ::WSAGetLastError(),
406  System::lastErrorMsg(true)).c_str());
407  }
408  break;
409  }
410  } // while (true) - waiting for connection event
411  } // if SOCKET_ERROR on connect
412 
413  // Set socket back to blocking
414  if(::WSAEventSelect(m_sockfd, m_event, 0) != 0)
415  {
416  _closeSocket(m_sockfd);
417  BLOCXX_THROW(SocketException,
418  Format("Resetting socket with WSAEventSelect Failed: %1",
419  System::lastErrorMsg(true)).c_str());
420  }
421  u_long ioctlarg = 0;
422  ::ioctlsocket(m_sockfd, FIONBIO, &ioctlarg);
423 
424  m_isConnected = true;
425 
426  m_peerAddress = addr; // To get the hostname from addr
427 
428  BLOCXX_ASSERT(addr.getType() == SocketAddress::INET);
429 
431 }
432 
434 void
436 {
437  if(m_in)
438  {
439  m_in.clear(ios::eofbit);
440  }
441  if(m_out)
442  {
443  m_out.clear(ios::eofbit);
444  }
445  if(!m_inout.fail())
446  {
447  m_inout.clear(ios::eofbit);
448  }
449 
450  ::SetEvent(m_event);
451  _closeSocket(m_sockfd);
452  m_isConnected = false;
453 }
454 
456 void
458 {
459  socklen_t len;
460  InetSocketAddress_t addr;
461  ::memset(&addr, 0, sizeof(addr));
462  len = sizeof(addr);
463  bool gotAddr = false;
464 
465  if (m_sockfd != INVALID_SOCKET)
466  {
467  len = sizeof(addr);
468  if (::getsockname(m_sockfd,
469  reinterpret_cast<struct sockaddr*>(&addr), &len) != SOCKET_ERROR)
470  {
472  }
473  else if (getAddrFromIface(addr) == 0)
474  {
475  len = sizeof(addr);
477  }
478 
479  len = sizeof(addr);
480  if (::getpeername(m_sockfd, reinterpret_cast<struct sockaddr*>(&addr),
481  &len) != SOCKET_ERROR)
482  {
484  }
485  }
486  else if (getAddrFromIface(addr) == 0)
487  {
489  }
490 }
491 
492 static Mutex guard;
494 int
495 SocketBaseImpl::write(const void* dataOut, int dataOutLen, ErrorAction errorAsException)
496 {
497  int rc = 0;
498  bool isError = false;
499  if (m_isConnected)
500  {
501  isError = waitForOutput(m_sendTimeout);
502  if (isError)
503  {
504  rc = -1;
505  }
506  else
507  {
508  rc = writeAux(dataOut, dataOutLen);
509  if (!m_traceFileOut.empty() && rc > 0)
510  {
511  MutexLock ml(guard);
512  ofstream traceFile(m_traceFileOut.c_str(), std::ios::app);
513  if (!traceFile)
514  {
515  BLOCXX_THROW(IOException, "Failed opening socket dump file");
516  }
517  if (!traceFile.write(static_cast<const char*>(dataOut), rc))
518  {
519  BLOCXX_THROW(IOException, "Failed writing to socket dump");
520  }
521 
522  ofstream comboTraceFile(String(m_traceFileOut + "Combo").c_str(), std::ios::app);
523  if (!comboTraceFile)
524  {
525  BLOCXX_THROW(IOException, "Failed opening socket dump file");
526  }
527  comboTraceFile << "\n--->Out " << rc << " bytes<---\n";
528  if (!comboTraceFile.write(static_cast<const char*>(dataOut), rc))
529  {
530  BLOCXX_THROW(IOException, "Failed writing to socket dump");
531  }
532  }
533  }
534  }
535  else
536  {
537  rc = -1;
538  }
539  if (rc < 0 && errorAsException)
540  {
541  BLOCXX_THROW(SocketException, "SocketBaseImpl::write");
542  }
543  return rc;
544 }
546 int
547 SocketBaseImpl::read(void* dataIn, int dataInLen, ErrorAction errorAsException)
548 {
549  int rc = 0;
550  bool isError = false;
551  if (m_isConnected)
552  {
553  isError = waitForInput(m_recvTimeout);
554  if (isError)
555  {
556  rc = -1;
557  }
558  else
559  {
560  rc = readAux(dataIn, dataInLen);
561  if (!m_traceFileIn.empty() && rc > 0)
562  {
563  MutexLock ml(guard);
564  ofstream traceFile(m_traceFileIn.c_str(), std::ios::app);
565  if (!traceFile)
566  {
567  BLOCXX_THROW(IOException, "Failed opening tracefile");
568  }
569  if (!traceFile.write(reinterpret_cast<const char*>(dataIn), rc))
570  {
571  BLOCXX_THROW(IOException, "Failed writing to socket dump");
572  }
573 
574  ofstream comboTraceFile(String(m_traceFileOut + "Combo").c_str(), std::ios::app);
575  if (!comboTraceFile)
576  {
577  BLOCXX_THROW(IOException, "Failed opening socket dump file");
578  }
579  comboTraceFile << "\n--->In " << rc << " bytes<---\n";
580  if (!comboTraceFile.write(reinterpret_cast<const char*>(dataIn), rc))
581  {
582  BLOCXX_THROW(IOException, "Failed writing to socket dump");
583  }
584  }
585  }
586  }
587  else
588  {
589  rc = -1;
590  }
591  if (rc < 0)
592  {
593  if (errorAsException)
594  BLOCXX_THROW(SocketException, "SocketBaseImpl::read");
595  }
596  return rc;
597 }
599 bool
600 SocketBaseImpl::waitForInput(const Timeout& timeOutSecs)
601 {
602  int rval = SocketUtils::waitForIO(m_sockfd, m_event, timeOutSecs, FD_READ);
603  if (rval == ETIMEDOUT)
604  {
605  m_recvTimeoutExprd = true;
606  }
607  else
608  {
609  m_recvTimeoutExprd = false;
610  }
611  return (rval != 0);
612 }
614 bool
615 SocketBaseImpl::waitForOutput(const Timeout& timeOutSecs)
616 {
617  return SocketUtils::waitForIO(m_sockfd, m_event, timeOutSecs,
618  FD_WRITE) != 0;
619 }
621 istream&
623 {
624  return m_in;
625 }
627 ostream&
629 {
630  return m_out;
631 }
633 iostream&
635 {
636  return m_inout;
637 }
639 // STATIC
640 void
641 SocketBaseImpl::setDumpFiles(const String& in, const String& out)
642 {
643  m_traceFileOut = out;
644  m_traceFileIn = in;
645 }
646 
647 } // end namespace BLOCXX_NAMESPACE
648 
649 #endif // #if defined(BLOCXX_WIN32)