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