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
46#include "blocxx/Format.hpp"
47#include "blocxx/Assertion.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"
55
56#include <cstdio>
57#include <cerrno>
58#include <fstream>
59#include <ws2tcpip.h>
60
61namespace
62{
63
64class SockInitializer
65{
66public:
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
80SockInitializer _sockInitializer;
81
83void
84_closeSocket(SOCKET& sockfd)
85{
86 if (sockfd != INVALID_SOCKET)
87 {
88 ::closesocket(sockfd);
89 sockfd = INVALID_SOCKET;
90 }
91}
92
94int
95getAddrFromIface(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
131namespace BLOCXX_NAMESPACE
132{
133
134using std::istream;
135using std::ostream;
136using std::iostream;
137using std::ifstream;
138using std::ofstream;
139using std::fstream;
140using std::ios;
143
145// static
146int
147SocketBaseImpl::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}
232SocketBaseImpl::SocketBaseImpl(SocketHandle_t fd,
233 SocketAddress::AddressType addrType)
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}
259SocketBaseImpl::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
286SocketBaseImpl::~SocketBaseImpl()
287{
288 try
289 {
290 disconnect();
291 }
292 catch (...)
293 {
294 // don't let exceptions escape
295 }
296 ::CloseHandle(m_event);
297}
300SocketBaseImpl::getSelectObj() const
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}
311void
312SocketBaseImpl::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
430 fillInetAddrParms();
431}
432
434void
435SocketBaseImpl::disconnect()
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
456void
457SocketBaseImpl::fillInetAddrParms()
458{
459 socklen_t len;
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 {
471 m_localAddress.assignFromNativeForm(&addr, len);
472 }
473 else if (getAddrFromIface(addr) == 0)
474 {
475 len = sizeof(addr);
476 m_localAddress.assignFromNativeForm(&addr, len);
477 }
478
479 len = sizeof(addr);
480 if (::getpeername(m_sockfd, reinterpret_cast<struct sockaddr*>(&addr),
481 &len) != SOCKET_ERROR)
482 {
483 m_peerAddress.assignFromNativeForm(&addr, len);
484 }
485 }
486 else if (getAddrFromIface(addr) == 0)
487 {
488 m_localAddress.assignFromNativeForm(&addr, len);
489 }
490}
491
492static Mutex guard;
494int
495SocketBaseImpl::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}
546int
547SocketBaseImpl::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}
599bool
600SocketBaseImpl::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}
614bool
615SocketBaseImpl::waitForOutput(const Timeout& timeOutSecs)
616{
617 return SocketUtils::waitForIO(m_sockfd, m_event, timeOutSecs,
618 FD_WRITE) != 0;
619}
621istream&
622SocketBaseImpl::getInputStream()
623{
624 return m_in;
625}
627ostream&
628SocketBaseImpl::getOutputStream()
629{
630 return m_out;
631}
633iostream&
634SocketBaseImpl::getIOStream()
635{
636 return m_inout;
637}
639// STATIC
640void
641SocketBaseImpl::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)
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
unsigned socklen_t
#define INVALID_SOCKET
#define ETIMEDOUT
static ShutDownMechanism_t getShutDownMechanism()
Definition Socket.hpp:296
Taken from RFC 1321.
sockaddr_in InetSocketAddress_t