blocxx
SocketBaseImpl.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
38#include "blocxx/BLOCXX_config.h"
39
40#if !defined(BLOCXX_WIN32)
41
44#include "blocxx/Format.hpp"
45#include "blocxx/Assertion.hpp"
47#include "blocxx/Mutex.hpp"
48#include "blocxx/MutexLock.hpp"
51#include "blocxx/Socket.hpp"
52#include "blocxx/Thread.hpp"
53#include "blocxx/DateTime.hpp"
56#include "blocxx/Logger.hpp"
57#include "blocxx/Select.hpp"
58
59
60extern "C"
61{
62#include <sys/types.h>
63#include <sys/time.h>
64#include <sys/socket.h>
65#include <sys/stat.h>
66#include <netdb.h>
67#include <arpa/inet.h>
68#include <unistd.h>
69#include <fcntl.h>
70#include <netinet/in.h>
71}
72
73#include <fstream>
74#include <cerrno>
75#include <cstdio>
76
77namespace BLOCXX_NAMESPACE
78{
79
80using std::istream;
81using std::ostream;
82using std::iostream;
83using std::ifstream;
84using std::ofstream;
85using std::fstream;
86using std::ios;
87
88namespace
89{
90static GlobalMutex g_guard = BLOCXX_GLOBAL_MUTEX_INIT();
91}
92
95
99 , IOIFC()
100 , m_isConnected(false)
101 , m_sockfd(-1)
102 , m_localAddress()
103 , m_peerAddress()
104 , m_recvTimeoutExprd(false)
105 , m_streamBuf(this)
106 , m_in(&m_streamBuf)
107 , m_out(&m_streamBuf)
108 , m_inout(&m_streamBuf)
109 , m_recvTimeout(Timeout::infinite)
110 , m_sendTimeout(Timeout::infinite)
111 , m_connectTimeout(Timeout::infinite)
112{
113 m_out.exceptions(std::ios::badbit);
114 m_inout.exceptions(std::ios::badbit);
115}
119 : SelectableIFC()
120 , IOIFC()
121 , m_isConnected(true)
122 , m_sockfd(fd)
123 , m_localAddress(SocketAddress::getAnyLocalHost())
124 , m_peerAddress(SocketAddress::allocEmptyAddress(addrType))
125 , m_recvTimeoutExprd(false)
126 , m_streamBuf(this)
127 , m_in(&m_streamBuf)
128 , m_out(&m_streamBuf)
129 , m_inout(&m_streamBuf)
130 , m_recvTimeout(Timeout::infinite)
131 , m_sendTimeout(Timeout::infinite)
132 , m_connectTimeout(Timeout::infinite)
133{
134 m_out.exceptions(std::ios::badbit);
135 m_inout.exceptions(std::ios::badbit);
136 if (addrType == SocketAddress::INET)
137 {
139 }
140 else if (addrType == SocketAddress::UDS)
141 {
143 }
144 else
145 {
146 BLOCXX_ASSERT(0);
147 }
148}
151 : SelectableIFC()
152 , IOIFC()
153 , m_isConnected(false)
154 , m_sockfd(-1)
155 , m_localAddress(SocketAddress::getAnyLocalHost())
156 , m_peerAddress(addr)
157 , m_recvTimeoutExprd(false)
158 , m_streamBuf(this)
159 , m_in(&m_streamBuf)
160 , m_out(&m_streamBuf)
161 , m_inout(&m_streamBuf)
162 , m_recvTimeout(Timeout::infinite)
163 , m_sendTimeout(Timeout::infinite)
164 , m_connectTimeout(Timeout::infinite)
165{
166 m_out.exceptions(std::ios::badbit);
167 m_inout.exceptions(std::ios::badbit);
169}
172{
173 try
174 {
175 disconnect();
176 }
177 catch (...)
178 {
179 // don't let exceptions escape
180 }
181}
185{
186 return m_sockfd;
187}
189void
191{
192 if (m_isConnected)
193 {
194 disconnect();
195 }
197 m_in.clear();
198 m_out.clear();
199 m_inout.clear();
200 BLOCXX_ASSERT(m_sockfd == -1);
202
203 int domain_type = PF_UNIX;
204 if( addr.getType() == SocketAddress::INET )
205 {
206 domain_type = PF_INET;
207#ifdef BLOCXX_HAVE_IPV6
208 // set PF_INET6 domain type for IPV6 protocol
209 if( reinterpret_cast<const sockaddr*>(addr.getInetAddress())->sa_family == AF_INET6)
210 {
211 domain_type = PF_INET6;
212 }
213#endif
214 }
215
216 AutoDescriptor sockfd(::socket(domain_type, SOCK_STREAM, 0));
217 if (sockfd.get() == -1)
218 {
220 "Failed to create a socket");
221 }
222
223 // set the close on exec flag so child process can't keep the socket.
224 if (::fcntl(sockfd.get(), F_SETFD, FD_CLOEXEC) == -1)
225 {
226 BLOCXX_THROW_ERRNO_MSG(SocketException, "SocketBaseImpl::connect() failed to set close-on-exec flag on socket");
227 }
228 int n;
229 int flags = ::fcntl(sockfd.get(), F_GETFL, 0);
230 ::fcntl(sockfd.get(), F_SETFL, flags | O_NONBLOCK);
231#if defined(BLOCXX_NCR)
232 if ((n = ::connect(sockfd.get(), const_cast<SocketAddress_t *>(addr.getNativeForm()), addr.getNativeFormSize())) < 0)
233#else
234 if ((n = ::connect(sockfd.get(), addr.getNativeForm(), addr.getNativeFormSize())) < 0)
235#endif
236 {
237 if (errno != EINPROGRESS)
238 {
240 Format("Failed to connect to: %1", addr.toString()).c_str());
241 }
242 }
243 if (n == -1)
244 {
245 // because of the above check for EINPROGRESS
246 // not connected yet, need to select and wait for connection to complete.
247 PosixUnnamedPipeRef lUPipe;
248 int pipefd = -1;
250 {
252 lUPipe = foo.cast_to<PosixUnnamedPipe>();
253 BLOCXX_ASSERT(lUPipe);
254 pipefd = lUPipe->getInputHandle();
255 }
257 Select::SelectObject sockSo(sockfd.get());
258 sockSo.waitForRead = true;
259 sockSo.waitForWrite = true;
260 selra.push_back(sockSo);
261 if (pipefd != -1)
262 {
263 Select::SelectObject pipeSo(pipefd);
264 pipeSo.waitForRead = true;
265 selra.push_back(pipeSo);
266 }
267 // here we spin checking for thread cancellation every so often.
269 timer.start();
270 do
271 {
273 n = Select::selectRW(selra, timer.asRelativeTimeout(0.1));
274 timer.loop();
275 } while (n == Select::SELECT_TIMEOUT && !timer.expired());
276
277 if (timer.expired())
278 {
279 BLOCXX_THROW(SocketException, "SocketBaseImpl::connect() select timedout");
280 }
281 else if (n == Select::SELECT_ERROR)
282 {
283 if (errno == EINTR)
284 {
286 }
287 BLOCXX_THROW_ERRNO_MSG(SocketException, "SocketBaseImpl::connect() select failed");
288 }
289
290 if (selra.size() == 2 && selra[1].readAvailable)
291 {
292 BLOCXX_THROW(SocketException, "Sockets have been shutdown");
293 }
294 else if (selra[0].readAvailable || selra[0].writeAvailable)
295 {
296 int error = 0;
297 socklen_t len = sizeof(error);
298#if defined(BLOCXX_NCR)
299 if (::getsockopt(sockfd.get(), SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
300#else
301 if (::getsockopt(sockfd.get(), SOL_SOCKET, SO_ERROR, &error, &len) < 0)
302#endif
303 {
305 "SocketBaseImpl::connect() getsockopt() failed");
306 }
307 if (error != 0)
308 {
309 errno = error;
311 "SocketBaseImpl::connect() failed");
312 }
313 }
314 else
315 {
316 BLOCXX_THROW(SocketException, "SocketBaseImpl::connect(). Logic error, sockfd not in FD set.");
317 }
318 }
319 ::fcntl(sockfd.get(), F_SETFL, flags);
320 m_sockfd = sockfd.release();
321 m_isConnected = true;
322 m_peerAddress = addr; // To get the hostname from addr
323 if (addr.getType() == SocketAddress::INET)
324 {
326 }
327 else if (addr.getType() == SocketAddress::UDS)
328 {
330 }
331 else
332 {
333 BLOCXX_ASSERT(0);
334 }
335
336 if (!m_traceFileOut.empty())
337 {
338 MutexLock ml(g_guard);
339
340 String combofilename = m_traceFileOut + "Combo";
341 ofstream comboTraceFile(combofilename.c_str(), std::ios::app);
342 if (!comboTraceFile)
343 {
344 BLOCXX_THROW_ERRNO_MSG(IOException, Format("Failed opening socket dump file \"%1\"", combofilename));
345 }
346 DateTime curDateTime;
347 curDateTime.setToCurrent();
348 comboTraceFile << Format("\n--->fd: %1 opened to \"%2\" at %3.%4 <---\n", getfd(),
349 addr.toString(),
350 curDateTime.toString("%X"), curDateTime.getMicrosecond());
351 }
352}
354void
356{
357 if (m_in)
358 {
359 m_in.clear(ios::eofbit);
360 }
361 if (m_out)
362 {
363 m_out.clear(ios::eofbit);
364 }
365 if (m_inout)
366 {
367 m_inout.clear(ios::eofbit);
368 }
369 if (m_sockfd != -1 && m_isConnected)
370 {
371 if (::close(m_sockfd) == -1)
372 {
373 int lerrno = errno;
374 Logger lgr("blocxx");
375 BLOCXX_LOG_ERROR(lgr, Format("Closing socket handle %1 failed: %2", m_sockfd, lerrno));
376 }
377 m_isConnected = false;
378
379 if (!m_traceFileOut.empty())
380 {
381 MutexLock ml(g_guard);
382
383 String combofilename = m_traceFileOut + "Combo";
384 ofstream comboTraceFile(combofilename.c_str(), std::ios::app);
385 if (!comboTraceFile)
386 {
387 BLOCXX_THROW_ERRNO_MSG(IOException, Format("Failed opening socket dump file \"%1\"", combofilename));
388 }
389 DateTime curDateTime;
390 curDateTime.setToCurrent();
391 comboTraceFile << "\n--->fd: " << getfd() << " closed at " << curDateTime.toString("%X") <<
392 '.' << curDateTime.getMicrosecond() << "<---\n";
393 }
394
395 m_sockfd = -1;
396 }
397}
399// JBW this needs reworked.
400void
402{
403 // create LocalAddress and PeerAddress structures for IPV6 protocol
404 socklen_t len;
405 struct sockaddr *p_addr;
407 memset(&ss, 0, sizeof(ss));
408 len = sizeof(ss);
409 p_addr = reinterpret_cast<struct sockaddr*>(&ss);
410 if (getsockname(m_sockfd, p_addr, &len) != -1)
411 {
413 }
414 memset(&ss, 0, sizeof(ss));
415 len = sizeof(ss);
416 if (getpeername(m_sockfd, p_addr, &len) != -1)
417 {
419 }
420}
422void
424{
425 socklen_t len;
427 memset(&addr, 0, sizeof(addr));
428 len = sizeof(addr);
429 if (getsockname(m_sockfd, reinterpret_cast<struct sockaddr*>(&addr), &len) == -1)
430 {
431 BLOCXX_THROW_ERRNO_MSG(SocketException, "SocketBaseImpl::fillUnixAddrParms: getsockname");
432 }
435}
437int
438SocketBaseImpl::write(const void* dataOut, int dataOutLen, ErrorAction errorAsException)
439{
440 int rc = 0;
441 bool isError = false;
442 if (m_isConnected)
443 {
444 isError = waitForOutput(m_sendTimeout);
445 if (isError)
446 {
447 rc = -1;
448 }
449 else
450 {
451 rc = writeAux(dataOut, dataOutLen);
452 if (!m_traceFileOut.empty() && rc > 0)
453 {
454 MutexLock ml(g_guard);
455 ofstream traceFile(m_traceFileOut.c_str(), std::ios::app);
456 if (!traceFile)
457 {
458 BLOCXX_THROW_ERRNO_MSG(IOException, Format("Failed opening socket dump file \"%1\"", m_traceFileOut));
459 }
460 if (!traceFile.write(static_cast<const char*>(dataOut), rc))
461 {
462 BLOCXX_THROW_ERRNO_MSG(IOException, "Failed writing to socket dump");
463 }
464
465 String combofilename = m_traceFileOut + "Combo";
466 ofstream comboTraceFile(combofilename.c_str(), std::ios::app);
467 if (!comboTraceFile)
468 {
469 BLOCXX_THROW_ERRNO_MSG(IOException, Format("Failed opening socket dump file \"%1\"", combofilename));
470 }
471 DateTime curDateTime;
472 curDateTime.setToCurrent();
473 comboTraceFile << "\n--->fd: " << getfd() << " Out " << rc << " bytes at " << curDateTime.toString("%X") <<
474 '.' << curDateTime.getMicrosecond() << "<---\n";
475 if (!comboTraceFile.write(static_cast<const char*>(dataOut), rc))
476 {
477 BLOCXX_THROW_ERRNO_MSG(IOException, "Failed writing to socket dump");
478 }
479 }
480 }
481 }
482 else
483 {
484 rc = -1;
485 }
486 if (rc < 0 && errorAsException == E_THROW_ON_ERROR)
487 {
488 BLOCXX_THROW_ERRNO_MSG(SocketException, "SocketBaseImpl::write");
489 }
490 return rc;
491}
493int
494SocketBaseImpl::read(void* dataIn, int dataInLen, ErrorAction errorAsException)
495{
496 int rc = 0;
497 bool isError = false;
498 if (m_isConnected)
499 {
500 isError = waitForInput(m_recvTimeout);
501 if (isError)
502 {
503 rc = -1;
504 }
505 else
506 {
507 rc = readAux(dataIn, dataInLen);
508 if (!m_traceFileIn.empty() && rc > 0)
509 {
510 MutexLock ml(g_guard);
511 ofstream traceFile(m_traceFileIn.c_str(), std::ios::app);
512 if (!traceFile)
513 {
514 BLOCXX_THROW_ERRNO_MSG(IOException, Format("Failed opening tracefile \"%1\"", m_traceFileIn));
515 }
516 if (!traceFile.write(reinterpret_cast<const char*>(dataIn), rc))
517 {
518 BLOCXX_THROW_ERRNO_MSG(IOException, "Failed writing to socket dump");
519 }
520
521 String combofilename = m_traceFileOut + "Combo";
522 ofstream comboTraceFile(combofilename.c_str(), std::ios::app);
523 if (!comboTraceFile)
524 {
525 BLOCXX_THROW_ERRNO_MSG(IOException, Format("Failed opening socket dump file \"%1\"", combofilename));
526 }
527 DateTime curDateTime;
528 curDateTime.setToCurrent();
529 comboTraceFile << "\n--->fd: " << getfd() << " In " << rc << " bytes at " << curDateTime.toString("%X") <<
530 '.' << curDateTime.getMicrosecond() << "<---\n";
531 if (!comboTraceFile.write(reinterpret_cast<const char*>(dataIn), rc))
532 {
533 BLOCXX_THROW_ERRNO_MSG(IOException, "Failed writing to socket dump");
534 }
535 }
536 }
537 }
538 else
539 {
540 rc = -1;
541 }
542 if (rc < 0)
543 {
544 if (errorAsException == E_THROW_ON_ERROR)
545 {
546 BLOCXX_THROW_ERRNO_MSG(SocketException, "SocketBaseImpl::read");
547 }
548 }
549 return rc;
550}
552bool
554{
556 if (rval == ETIMEDOUT)
557 {
558 m_recvTimeoutExprd = true;
559 }
560 else
561 {
562 m_recvTimeoutExprd = false;
563 }
564 return (rval != 0);
565}
567bool
573istream&
575{
576 return m_in;
577}
579ostream&
585iostream&
587{
588 return m_inout;
589}
591// STATIC
592void
594{
595 m_traceFileOut = out;
596 m_traceFileIn = in;
597}
598
599} // end namespace BLOCXX_NAMESPACE
600
601#endif // #if !defined(BLOCXX_WIN32)
602
#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.
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
#define BLOCXX_GLOBAL_MUTEX_INIT()
#define BLOCXX_LOG_ERROR(logger, message)
Log message to logger with the Error level.
Definition Logger.hpp:433
unsigned socklen_t
#define ETIMEDOUT
Array<> wraps std::vector<> in COWReference<> adding ref counting and copy on write capability.
Definition ArrayFwd.hpp:46
void push_back(const T &x)
Append an element to the end of the Array.
size_type size() const
PURPOSE: The AutoResource class template is an analog of std::auto_ptr for managing arbitrary resourc...
handle_type get() const
Return handle of resource, retaining ownership.
handle_type release()
Relinquish ownership of resource and return its handle.
The DateTime class is an abstraction for date time data.
Definition DateTime.hpp:81
UInt32 getMicrosecond() const
Get the microsecond of the second for this DateTime object.
String toString(ETimeOffset timeOffset=E_LOCAL_TIME) const
void setToCurrent()
Set this DateTime to the current system time.
const char * c_str() const
Definition Format.cpp:55
IntrusiveReference< U > cast_to() const
Logging interface.
Definition Logger.hpp:87
void assignFromNativeForm(const UnixSocketAddress_t *address, size_t len)
const SocketAddress_t * getNativeForm() const
const InetSocketAddress_t * getInetAddress() const
Get a pointer to the InetSocketAddress_t precondition: getType() == INET.
const String toString() const
Returns the IP address and the port with a colon in between.
virtual void connect(const SocketAddress &addr)
virtual int readAux(void *dataIn, int dataInLen)=0
virtual bool waitForInput(const Timeout &timeout)
virtual int writeAux(const void *dataOut, int dataOutLen)=0
bool waitForOutput(const Timeout &timeout)
int write(const void *dataOut, int dataOutLen, ErrorAction errorAsException=E_RETURN_ON_ERROR)
Write a specified number of bytes to the device that is exposing the IOIFC interface.
static void setDumpFiles(const String &in, const String &out)
int read(void *dataIn, int dataInLen, ErrorAction errorAsException=E_RETURN_ON_ERROR)
Read a specified number of bytes from the device that is exposing the IOIFC interface.
static ShutDownMechanism_t getShutDownMechanism()
Definition Socket.hpp:296
This String class is an abstract data type that represents as NULL terminated string of characters.
Definition String.hpp:67
const char * c_str() const
Definition String.cpp:905
static void testCancel()
Test if this thread has been cancelled.
Definition Thread.cpp:432
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition Timeout.hpp:56
A TimeoutTimer is used by an algorithm to determine when a timeout has expired.
bool expired() const
Indicates whether the last loop time has exceeded the timeout.
void start()
Meant to be called by timeout functions which loop.
void loop()
Meant to be called by timeout functions which loop, but don't want to reset the interval.
const int SELECT_TIMEOUT
The value returned from select when the timeout value has expired.
Definition Select.hpp:59
int selectRW(SelectObjectArray &selarray, UInt32 ms)
Definition Select.cpp:92
const int SELECT_ERROR
The value returned from select when any error occurs other than timeout.
Definition Select.hpp:63
int waitForIO(SocketHandle_t fd, int timeOutSecs, SocketFlags::EWaitDirectionFlag waitFlag)
Wait for input or output on a socket.
Taken from RFC 1321.
sockaddr_in InetSocketAddress_t
sockaddr SocketAddress_t
sockaddr_un UnixSocketAddress_t
LazyGlobal< Mutex, int, GlobalMutexFactory > GlobalMutex
bool waitForRead
Input parameter. Set it to true to indicate that waiting for read availability on s is desired.
Definition Select.hpp:103
bool waitForWrite
Input parameter. Set it to true to indicate that waiting for write availability on s is desired.
Definition Select.hpp:105