LCOV - code coverage report
Current view: top level - net - tcpclient.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 32 53 60.4 %
Date: 2011-08-21 Functions: 1 1 100.0 %
Branches: 13 30 43.3 %

           Branch data     Line data    Source code
       1                 :            : /* tcpclient.cc: Open a TCP connection to a server.
       2                 :            :  *
       3                 :            :  * Copyright 1999,2000,2001 BrightStation PLC
       4                 :            :  * Copyright 2002 Ananova Ltd
       5                 :            :  * Copyright 2004,2005,2006,2007,2008,2010 Olly Betts
       6                 :            :  *
       7                 :            :  * This program is free software; you can redistribute it and/or
       8                 :            :  * modify it under the terms of the GNU General Public License as
       9                 :            :  * published by the Free Software Foundation; either version 2 of the
      10                 :            :  * License, or (at your option) any later version.
      11                 :            :  *
      12                 :            :  * This program is distributed in the hope that it will be useful,
      13                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15                 :            :  * GNU General Public License for more details.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU General Public License
      18                 :            :  * along with this program; if not, write to the Free Software
      19                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
      20                 :            :  * USA
      21                 :            :  */
      22                 :            : 
      23                 :            : #include <config.h>
      24                 :            : 
      25                 :            : #include "remoteconnection.h"
      26                 :            : #include "tcpclient.h"
      27                 :            : #include <xapian/error.h>
      28                 :            : 
      29                 :            : #include "safeerrno.h"
      30                 :            : #include "safefcntl.h"
      31                 :            : #include "socket_utils.h"
      32                 :            : 
      33                 :            : #include <cmath>
      34                 :            : #include <cstring>
      35                 :            : #ifndef __WIN32__
      36                 :            : # include <netdb.h>
      37                 :            : # include <netinet/in.h>
      38                 :            : # include <netinet/tcp.h>
      39                 :            : # include <sys/socket.h>
      40                 :            : # include "safesysselect.h"
      41                 :            : #endif
      42                 :            : 
      43                 :            : using namespace std;
      44                 :            : 
      45                 :            : int
      46                 :        771 : TcpClient::open_socket(const std::string & hostname, int port,
      47                 :            :                        double timeout_connect, bool tcp_nodelay)
      48                 :            : {
      49                 :            :     // FIXME: timeout on gethostbyname() ?
      50                 :        771 :     struct hostent *host = gethostbyname(hostname.c_str());
      51                 :            : 
      52         [ -  + ]:        771 :     if (host == 0) {
      53                 :            :         throw Xapian::NetworkError(std::string("Couldn't resolve host ") + hostname,
      54                 :            : #ifdef __WIN32__
      55                 :            :                 socket_errno()
      56                 :            : #else
      57                 :            :                 // "socket_errno()" is just errno on UNIX which is
      58                 :            :                 // inappropriate here - if gethostbyname() returns NULL an
      59                 :            :                 // error code is available in h_errno (with values
      60                 :            :                 // incompatible with errno).  On Linux at least, if h_errno
      61                 :            :                 // is < 0, then the error code *IS* in errno!
      62                 :            :                 (h_errno < 0 ? errno : -h_errno)
      63                 :            : #endif
      64         [ #  # ]:          0 :                 );
      65                 :            :     }
      66                 :            : 
      67                 :        771 :     int socketfd = socket(PF_INET, SOCK_STREAM, 0);
      68                 :            : 
      69         [ -  + ]:        771 :     if (socketfd < 0) {
      70                 :          0 :         throw Xapian::NetworkError("Couldn't create socket", socket_errno());
      71                 :            :     }
      72                 :            : 
      73                 :            :     struct sockaddr_in remaddr;
      74                 :        771 :     memset(&remaddr, 0, sizeof(remaddr));
      75                 :        771 :     remaddr.sin_family = AF_INET;
      76                 :        771 :     remaddr.sin_port = htons(port);
      77                 :        771 :     memcpy(&remaddr.sin_addr, host->h_addr, host->h_length);
      78                 :            : 
      79                 :            : #ifdef __WIN32__
      80                 :            :     ULONG enabled = 1;
      81                 :            :     int rc = ioctlsocket(socketfd, FIONBIO, &enabled);
      82                 :            : #else
      83                 :        771 :     int rc = fcntl(socketfd, F_SETFL, O_NDELAY);
      84                 :            : #endif
      85         [ -  + ]:        771 :     if (rc < 0) {
      86                 :          0 :         int saved_errno = socket_errno(); // note down in case close hits an error
      87                 :          0 :         close_fd_or_socket(socketfd);
      88                 :          0 :         throw Xapian::NetworkError("Couldn't set O_NDELAY", saved_errno);
      89                 :            :     }
      90                 :            : 
      91         [ +  - ]:        771 :     if (tcp_nodelay) {
      92                 :        771 :         int optval = 1;
      93                 :            :         // 4th argument might need to be void* or char* - cast it to char*
      94                 :            :         // since C++ allows implicit conversion to void* but not from void*.
      95         [ -  + ]:        771 :         if (setsockopt(socketfd, IPPROTO_TCP, TCP_NODELAY,
      96                 :            :                        reinterpret_cast<char *>(&optval),
      97                 :            :                        sizeof(optval)) < 0) {
      98                 :          0 :             int saved_errno = socket_errno(); // note down in case close hits an error
      99                 :          0 :             close_fd_or_socket(socketfd);
     100                 :          0 :             throw Xapian::NetworkError("Couldn't set TCP_NODELAY", saved_errno);
     101                 :            :         }
     102                 :            :     }
     103                 :            : 
     104                 :            :     int retval = connect(socketfd, reinterpret_cast<sockaddr *>(&remaddr),
     105                 :        771 :                          sizeof(remaddr));
     106                 :            : 
     107         [ +  - ]:        771 :     if (retval < 0) {
     108                 :            : #ifdef __WIN32__
     109                 :            :         if (WSAGetLastError() != WSAEWOULDBLOCK) {
     110                 :            : #else
     111         [ -  + ]:        771 :         if (socket_errno() != EINPROGRESS) {
     112                 :            : #endif
     113                 :          0 :             int saved_errno = socket_errno(); // note down in case close hits an error
     114                 :          0 :             close_fd_or_socket(socketfd);
     115                 :          0 :             throw Xapian::NetworkError("Couldn't connect (1)", saved_errno);
     116                 :            :         }
     117                 :            : 
     118                 :            :         // wait for input to be available.
     119                 :            :         fd_set fdset;
     120                 :        771 :         FD_ZERO(&fdset);
     121                 :        771 :         FD_SET(socketfd, &fdset);
     122                 :            : 
     123   [ -  +  #  # ]:        771 :         do {
                 [ -  + ]
     124                 :            :             // FIXME: Reduce the timeout if we retry on EINTR.
     125                 :            :             struct timeval tv;
     126                 :        771 :             tv.tv_sec = long(timeout_connect);
     127                 :        771 :             tv.tv_usec = long(std::fmod(timeout_connect, 1.0) * 1e6);
     128                 :            : 
     129                 :        771 :             retval = select(socketfd + 1, 0, &fdset, &fdset, &tv);
     130                 :            :         } while (retval < 0 && errno == EINTR);
     131                 :            : 
     132         [ -  + ]:        771 :         if (retval < 0) {
     133                 :          0 :             int saved_errno = errno;
     134                 :          0 :             close_fd_or_socket(socketfd);
     135                 :          0 :             throw Xapian::NetworkError("Couldn't connect (2)", saved_errno);
     136                 :            :         }
     137                 :            : 
     138         [ -  + ]:        771 :         if (retval <= 0) {
     139                 :          0 :             close_fd_or_socket(socketfd);
     140                 :          0 :             throw Xapian::NetworkTimeoutError("Timed out waiting to connect", ETIMEDOUT);
     141                 :            :         }
     142                 :            : 
     143                 :        771 :         int err = 0;
     144                 :        771 :         SOCKLEN_T len = sizeof(err);
     145                 :            : 
     146                 :            :         // 4th argument might need to be void* or char* - cast it to char*
     147                 :            :         // since C++ allows implicit conversion to void* but not from void*.
     148                 :            :         retval = getsockopt(socketfd, SOL_SOCKET, SO_ERROR,
     149                 :        771 :                             reinterpret_cast<char *>(&err), &len);
     150                 :            : 
     151         [ -  + ]:        771 :         if (retval < 0) {
     152                 :          0 :             int saved_errno = socket_errno(); // note down in case close hits an error
     153                 :          0 :             close_fd_or_socket(socketfd);
     154                 :          0 :             throw Xapian::NetworkError("Couldn't get socket options", saved_errno);
     155                 :            :         }
     156         [ -  + ]:        771 :         if (err) {
     157                 :          0 :             close_fd_or_socket(socketfd);
     158                 :          0 :             throw Xapian::NetworkError("Couldn't connect (3)", err);
     159                 :            :         }
     160                 :            :     }
     161                 :            : 
     162                 :            : #ifdef __WIN32__
     163                 :            :     enabled = 0;
     164                 :            :     ioctlsocket(socketfd, FIONBIO, &enabled);
     165                 :            : #else
     166                 :        771 :     fcntl(socketfd, F_SETFL, 0);
     167                 :            : #endif
     168                 :        771 :     return socketfd;
     169                 :            : }

Generated by: LCOV version 1.8