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{
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);
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);
201 BLOCXX_ASSERT(addr.getType() == SocketAddress::INET || addr.getType() == SocketAddress::UDS);
202
203 int domain_type = PF_UNIX;
204 if( addr.getType() == SocketAddress::INET )
205 {
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 {
212 }
213#endif
214 }
215
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);
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.
248 int pipefd = -1;
250 {
252 lUPipe = foo.cast_to<PosixUnnamedPipe>();
254 pipefd = lUPipe->getInputHandle();
255 }
258 sockSo.waitForRead = true;
259 sockSo.waitForWrite = true;
260 selra.push_back(sockSo);
261 if (pipefd != -1)
262 {
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 {
339
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 }
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 {
382
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 }
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
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{
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
439{
440 int rc = 0;
441 bool isError = false;
442 if (m_isConnected)
443 {
445 if (isError)
446 {
447 rc = -1;
448 }
449 else
450 {
452 if (!m_traceFileOut.empty() && rc > 0)
453 {
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
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 }
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 }
487 {
488 BLOCXX_THROW_ERRNO_MSG(SocketException, "SocketBaseImpl::write");
489 }
490 return rc;
491}
493int
495{
496 int rc = 0;
497 bool isError = false;
498 if (m_isConnected)
499 {
501 if (isError)
502 {
503 rc = -1;
504 }
505 else
506 {
508 if (!m_traceFileIn.empty() && rc > 0)
509 {
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
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 }
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 {
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 Array.hpp:66
PURPOSE: The AutoResource class template is an analog of std::auto_ptr for managing arbitrary resourc...
The DateTime class is an abstraction for date time data.
Definition DateTime.hpp:81
void setToCurrent()
Set this DateTime to the current system time.
const char * c_str() const
Definition Format.cpp:55
Logging interface.
Definition Logger.hpp:87
void assignFromNativeForm(const UnixSocketAddress_t *address, size_t len)
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.
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
bool operator==(const Array< T > &x, const Array< T > &y)
sockaddr SocketAddress_t
sockaddr_un UnixSocketAddress_t
LazyGlobal< Mutex, int, GlobalMutexFactory > GlobalMutex