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 
43 #include "blocxx/SocketUtils.hpp"
44 #include "blocxx/Format.hpp"
45 #include "blocxx/Assertion.hpp"
46 #include "blocxx/IOException.hpp"
47 #include "blocxx/Mutex.hpp"
48 #include "blocxx/MutexLock.hpp"
49 #include "blocxx/GlobalMutex.hpp"
51 #include "blocxx/Socket.hpp"
52 #include "blocxx/Thread.hpp"
53 #include "blocxx/DateTime.hpp"
54 #include "blocxx/TimeoutTimer.hpp"
56 #include "blocxx/Logger.hpp"
57 #include "blocxx/Select.hpp"
58 
59 
60 extern "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 
77 namespace BLOCXX_NAMESPACE
78 {
79 
80 using std::istream;
81 using std::ostream;
82 using std::iostream;
83 using std::ifstream;
84 using std::ofstream;
85 using std::fstream;
86 using std::ios;
87 
88 namespace
89 {
90 static GlobalMutex g_guard = BLOCXX_GLOBAL_MUTEX_INIT();
91 }
92 
95 
98  : SelectableIFC()
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 }
183 Select_t
185 {
186  return m_sockfd;
187 }
189 void
191 {
192  if (m_isConnected)
193  {
194  disconnect();
195  }
196  m_streamBuf.reset();
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 }
354 void
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.
400 void
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 }
422 void
424 {
425  socklen_t len;
426  UnixSocketAddress_t addr;
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 }
437 int
438 SocketBaseImpl::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 }
493 int
494 SocketBaseImpl::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 }
552 bool
554 {
556  if (rval == ETIMEDOUT)
557  {
558  m_recvTimeoutExprd = true;
559  }
560  else
561  {
562  m_recvTimeoutExprd = false;
563  }
564  return (rval != 0);
565 }
567 bool
569 {
571 }
573 istream&
575 {
576  return m_in;
577 }
579 ostream&
581 {
582  return m_out;
583 }
585 iostream&
587 {
588  return m_inout;
589 }
591 // STATIC
592 void
594 {
595  m_traceFileOut = out;
596  m_traceFileIn = in;
597 }
598 
599 } // end namespace BLOCXX_NAMESPACE
600 
601 #endif // #if !defined(BLOCXX_WIN32)
602 
Array<> wraps std::vector<> in COWReference<> adding ref counting and copy on write capability...
Definition: Array.hpp:65
static void setDumpFiles(const String &in, const String &out)
String toString(ETimeOffset timeOffset=E_LOCAL_TIME) const
Definition: DateTime.cpp:1249
sockaddr_un UnixSocketAddress_t
A TimeoutTimer is used by an algorithm to determine when a timeout has expired.
const String toString() const
Returns the IP address and the port with a colon in between.
const SocketAddress_t * getNativeForm() const
IntrusiveReference< U > cast_to() const
void loop()
Meant to be called by timeout functions which loop, but don't want to reset the interval.
UInt32 getMicrosecond() const
Get the microsecond of the second for this DateTime object.
Definition: DateTime.cpp:1064
#define BLOCXX_LOG_ERROR(logger, message)
Log message to logger with the Error level.
Definition: Logger.hpp:433
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(), it throws an AssertionException.
Definition: Assertion.hpp:57
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.
BLOCXX_COMMON_API int close(const FileHandle &hdl)
Close file handle.
This String class is an abstract data type that represents as NULL terminated string of characters...
Definition: String.hpp:66
bool waitForOutput(const Timeout &timeout)
handle_type get() const
Return handle of resource, retaining ownership.
bool expired() const
Indicates whether the last loop time has exceeded the timeout.
virtual bool waitForInput(const Timeout &timeout)
handle_type release()
Relinquish ownership of resource and return its handle.
virtual int writeAux(const void *dataOut, int dataOutLen)=0
const int SELECT_ERROR
The value returned from select when any error occurs other than timeout.
Definition: Select.hpp:63
AddressType getType() const
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.
const int SELECT_TIMEOUT
The value returned from select when the timeout value has expired.
Definition: Select.hpp:59
void assignFromNativeForm(const UnixSocketAddress_t *address, size_t len)
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition: Timeout.hpp:55
PURPOSE: The AutoResource class template is an analog of std::auto_ptr for managing arbitrary resourc...
void setToCurrent()
Set this DateTime to the current system time.
Definition: DateTime.cpp:1197
const char * c_str() const
Definition: String.cpp:905
virtual int readAux(void *dataIn, int dataInLen)=0
void start()
Meant to be called by timeout functions which loop.
const InetSocketAddress_t * getInetAddress() const
Get a pointer to the InetSocketAddress_t precondition: getType() == INET.
bool waitForRead
Input parameter. Set it to true to indicate that waiting for read availability on s is desired...
Definition: Select.hpp:103
unsigned socklen_t
void push_back(const T &x)
Append an element to the end of the Array.
Definition: ArrayImpl.hpp:251
sockaddr_in InetSocketAddress_t
SocketHandle_t getfd() const
static ShutDownMechanism_t getShutDownMechanism()
Definition: Socket.hpp:296
Logging interface.
Definition: Logger.hpp:86
The DateTime class is an abstraction for date time data.
Definition: DateTime.hpp:80
Timeout asRelativeTimeout() const
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
Definition: Exception.hpp:263
sockaddr SocketAddress_t
LazyGlobal< Mutex, int, GlobalMutexFactory > GlobalMutex
Definition: GlobalMutex.hpp:56
int waitForIO(SocketHandle_t fd, int timeOutSecs, SocketFlags::EWaitDirectionFlag waitFlag)
Wait for input or output on a socket.
int selectRW(SelectObjectArray &selarray, UInt32 ms)
Definition: Select.cpp:92
bool empty() const
Definition: String.hpp:237
static void testCancel()
Test if this thread has been cancelled.
Definition: Thread.cpp:432
virtual void connect(const SocketAddress &addr)
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
Definition: Exception.hpp:312
#define ETIMEDOUT
Definition: SocketUtils.hpp:47
size_type size() const
Definition: ArrayImpl.hpp:160
#define BLOCXX_GLOBAL_MUTEX_INIT()
Definition: GlobalMutex.hpp:58