OPeNDAP Hyrax Back End Server (BES) Updated for version 3.8.3
TcpSocket.cc
Go to the documentation of this file.
00001 // TcpSocket.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include <ctype.h>
00034 #include <sys/types.h>
00035 #include <sys/socket.h>
00036 #include <netinet/in.h>
00037 #include <arpa/inet.h>
00038 #include <netdb.h>
00039 #include <fcntl.h>
00040 #include <netinet/tcp.h>
00041 
00042 #ifdef HAVE_LIBWRAP
00043 extern "C" {
00044 #include "tcpd.h"
00045 int allow_severity;
00046 int deny_severity;
00047 }
00048 #endif
00049 
00050 #include <cstring>
00051 #include <cerrno>
00052 
00053 #include <iostream>
00054 #include <sstream>
00055 
00056 using std::cerr ;
00057 using std::endl ;
00058 using std::istringstream ;
00059 
00060 #include "TcpSocket.h"
00061 #include "SocketConfig.h"
00062 #include "TheBESKeys.h"
00063 #include "BESDebug.h"
00064 #include "BESInternalError.h"
00065 #include "BESInternalFatalError.h"
00066 
00067 void
00068 TcpSocket::connect()
00069 {
00070     if( _listening )
00071     {
00072         string err( "Socket is already listening" ) ;
00073         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00074     }
00075 
00076     if( _connected )
00077     {
00078         string err( "Socket is already connected" ) ;
00079         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00080     }
00081 
00082     if( _host == "" )
00083         _host = "localhost" ;
00084 
00085     struct protoent *pProtoEnt ;
00086     struct sockaddr_in sin ;
00087     struct hostent *ph ;
00088     long address ;
00089     if( isdigit( _host[0] ) )
00090     {
00091         if( ( address = inet_addr( _host.c_str() ) ) == -1 )
00092         {
00093             string err( "Invalid host ip address " ) ;
00094             err += _host ;
00095             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00096         }
00097         sin.sin_addr.s_addr = address ;
00098         sin.sin_family = AF_INET ;
00099     }
00100     else
00101     {
00102         if( ( ph = gethostbyname( _host.c_str() ) ) == NULL )
00103         {
00104             switch( h_errno )
00105             {
00106                 case HOST_NOT_FOUND:
00107                     {
00108                         string err( "No such host " ) ;
00109                         err += _host ;
00110                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00111                     }
00112                 case TRY_AGAIN:
00113                     {
00114                         string err( "Host " ) ;
00115                         err += _host + " is busy, try again later" ;
00116                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00117                     }
00118                 case NO_RECOVERY:
00119                     {
00120                         string err( "DNS error for host " ) ;
00121                         err += _host ;
00122                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00123                     }
00124                 case NO_ADDRESS:
00125                     {
00126                         string err( "No IP address for host " ) ;
00127                         err += _host ;
00128                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00129                     }
00130                 default:
00131                     {
00132                         throw BESInternalError( "unknown error", __FILE__, __LINE__ ) ;
00133                     }
00134             }
00135         }
00136         else
00137         {
00138             sin.sin_family = ph->h_addrtype ;
00139             for( char **p =ph->h_addr_list; *p != NULL; p++ )
00140             {
00141                 struct in_addr in ;
00142                 (void)memcpy( &in.s_addr, *p, sizeof( in.s_addr ) ) ;
00143                 memcpy( (char*)&sin.sin_addr, (char*)&in, sizeof( in ) ) ;
00144             }
00145         }
00146     }
00147 
00148     sin.sin_port = htons( _portVal ) ;
00149     pProtoEnt = getprotobyname( "tcp" ) ;
00150     if( !pProtoEnt )
00151     {
00152         string err( "Error retreiving tcp protocol information" ) ;
00153         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00154     }
00155     
00156     _connected = false;
00157     int descript = socket( AF_INET, SOCK_STREAM, pProtoEnt->p_proto ) ;
00158 
00159     /*
00160     unsigned int sockbufsize = 0 ;
00161     int size = sizeof(int) ;
00162     int err = getsockopt( descript, IPPROTO_TCP, TCP_MAXSEG,
00163                           (void *)&sockbufsize, (socklen_t*)&size) ;
00164     cerr << "max size of tcp segment = " << sockbufsize << endl ;
00165     */
00166     
00167     if( descript == -1 ) 
00168     {
00169         string err("getting socket descriptor: ");
00170         const char* error_info = strerror(errno);
00171         if(error_info)
00172             err += (string)error_info;
00173         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00174     } else {
00175         long holder;
00176         _socket = descript;
00177 
00178         //set socket to non-blocking mode
00179         holder = fcntl(_socket, F_GETFL, NULL);
00180         holder = holder | O_NONBLOCK;
00181         fcntl(_socket, F_SETFL, holder);
00182     
00183         // we must set the send and receive buffer sizes before the connect call
00184         setTcpRecvBufferSize( ) ;
00185         setTcpSendBufferSize( ) ;
00186       
00187         int res = ::connect( descript, (struct sockaddr*)&sin, sizeof( sin ) );
00188 
00189       
00190         if( res == -1 ) 
00191         {
00192             if(errno == EINPROGRESS) {
00193           
00194                 fd_set write_fd ;
00195                 struct timeval timeout ;
00196                 int maxfd = _socket;
00197           
00198                 timeout.tv_sec = 5;
00199                 timeout.tv_usec = 0;
00200           
00201                 FD_ZERO( &write_fd);
00202                 FD_SET( _socket, &write_fd );
00203           
00204                 if( select( maxfd+1, NULL, &write_fd, NULL, &timeout) < 0 ) {
00205           
00206                     //reset socket to blocking mode
00207                     holder = fcntl(_socket, F_GETFL, NULL);
00208                     holder = holder & (~O_NONBLOCK);
00209                     fcntl(_socket, F_SETFL, holder);
00210             
00211                     //throw error - select could not resolve socket
00212                     string err( "selecting sockets: " ) ;
00213                     const char *error_info = strerror( errno ) ;
00214                     if( error_info )
00215                         err += (string)error_info ;
00216                     throw BESInternalError( err, __FILE__, __LINE__ ) ;
00217 
00218                 } 
00219                 else 
00220                 {
00221 
00222                     //check socket status
00223                     socklen_t lon;
00224                     int valopt;
00225                     lon = sizeof(int);
00226                     getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
00227             
00228                     if(valopt) 
00229                     {
00230 
00231                         //reset socket to blocking mode
00232                         holder = fcntl(_socket, F_GETFL, NULL);
00233                         holder = holder & (~O_NONBLOCK);
00234                         fcntl(_socket, F_SETFL, holder);
00235               
00236                         //throw error - did not successfully connect
00237                         string err("Did not successfully connect to server\n");
00238                         err += "Server may be down or you may be trying on the wrong port";
00239                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00240               
00241                     } 
00242                     else 
00243                     {
00244                         //reset socket to blocking mode
00245                         holder = fcntl(_socket, F_GETFL, NULL);
00246                         holder = holder & (~O_NONBLOCK);
00247                         fcntl(_socket, F_SETFL, holder);
00248               
00249                         //succesful connetion to server
00250                         _connected = true;
00251                     }
00252                 }
00253             } 
00254             else 
00255             {
00256 
00257                 //reset socket to blocking mode
00258                 holder = fcntl(_socket, F_GETFL, NULL);
00259                 holder = holder & (~O_NONBLOCK);
00260                 fcntl(_socket, F_SETFL, holder);
00261           
00262                 //throw error - errno was not EINPROGRESS
00263                 string err("socket connect: ");
00264                 const char* error_info = strerror(errno);
00265                 if(error_info)
00266                     err += (string)error_info;
00267                 throw BESInternalError( err, __FILE__, __LINE__ ) ;
00268             }
00269         }
00270         else
00271         {
00272             // The socket connect request completed immediately
00273             // even that the socket was in non-blocking mode
00274             
00275             //reset socket to blocking mode
00276             holder = fcntl(_socket, F_GETFL, NULL);
00277             holder = holder & (~O_NONBLOCK);
00278             fcntl(_socket, F_SETFL, holder);
00279             _connected = true;
00280         }
00281     }
00282 }
00283 
00284 void
00285 TcpSocket::listen()
00286 {
00287     if( _connected )
00288     {
00289         string err( "Socket is already connected" ) ;
00290         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00291     }
00292 
00293     if( _listening )
00294     {
00295         string err( "Socket is already listening" ) ;
00296         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00297     }
00298 
00299     int on = 1 ;
00300     struct sockaddr_in server ;
00301     server.sin_family = AF_INET ;
00302     server.sin_addr.s_addr = INADDR_ANY ;
00303     struct servent *sir = 0 ;
00304     sir = getservbyport( _portVal, "tcp" ) ;
00305     if( sir )
00306     {
00307         string error = sir->s_name + (string)" is using my socket" ;
00308         throw BESInternalError( error, __FILE__, __LINE__ ) ;
00309     }
00310     server.sin_port = htons( _portVal ) ;
00311     _socket = socket( AF_INET, SOCK_STREAM, 0 ) ;
00312     if( _socket != -1 )
00313     {
00314         if( setsockopt( _socket, SOL_SOCKET, SO_REUSEADDR,
00315                          (char*)&on, sizeof( on ) ) )
00316         {
00317             string error( "could not set SO_REUSEADDR on TCP socket" ) ;
00318             const char* error_info = strerror( errno ) ;
00319             if( error_info )
00320                 error += " " + (string)error_info ;
00321             throw BESInternalError( error, __FILE__, __LINE__ ) ;
00322         }
00323 
00324         if( bind( _socket, (struct sockaddr*)&server, sizeof server) != -1 )
00325         {
00326             int length = sizeof( server ) ;
00327 #ifdef _GETSOCKNAME_USES_SOCKLEN_T      
00328             if( getsockname( _socket, (struct sockaddr *)&server,
00329                              (socklen_t *)&length ) == -1 )
00330 #else
00331             if( getsockname( _socket, (struct sockaddr *)&server,
00332                              &length ) == -1 )
00333 #endif
00334             {
00335                 string error( "getting socket name" ) ;
00336                 const char* error_info = strerror( errno ) ;
00337                 if( error_info )
00338                     error += " " + (string)error_info ;
00339                 throw BESInternalError( error, __FILE__, __LINE__ ) ;
00340             }
00341 
00342             // The send and receive buffer sizes must be set before the call to
00343             // ::listen.
00344             setTcpRecvBufferSize( ) ;
00345             setTcpSendBufferSize( ) ;
00346 
00347             if( ::listen( _socket, 5 ) == 0 )
00348             {
00349                 _listening = true ;
00350             }
00351             else
00352             {
00353                 string error( "could not listen TCP socket" ) ;
00354                 const char* error_info = strerror( errno ) ;
00355                 if( error_info )
00356                     error += " " + (string)error_info ;
00357                 throw BESInternalError( error, __FILE__, __LINE__ ) ;
00358             }
00359         }
00360         else
00361         {
00362             string error( "could not bind TCP socket" ) ;
00363             const char* error_info = strerror( errno ) ;
00364             if( error_info )
00365                 error += " " + (string)error_info ;
00366             throw BESInternalError( error, __FILE__, __LINE__ ) ;
00367         }
00368     }
00369     else
00370     {
00371         string error( "could not create socket" ) ;
00372         const char *error_info = strerror( errno ) ;
00373         if( error_info )
00374             error += " " + (string)error_info ;
00375         throw BESInternalError( error, __FILE__, __LINE__ ) ;
00376     }
00377 }
00378 
00397 void
00398 TcpSocket::setTcpRecvBufferSize()
00399 {
00400     if( !_haveRecvBufferSize )
00401     {
00402         bool found = false ;
00403         string setit ;
00404         try
00405         {
00406             TheBESKeys::TheKeys()->get_value( "BES.SetSockRecvSize",
00407                                               setit, found);
00408         }
00409         catch( ... )
00410         {
00411             // ignore any exceptions caught trying to get this key. The
00412             // client also calls this function.
00413             setit = "No" ;
00414         }
00415         if( setit == "Yes" || setit == "yes" || setit == "Yes" )
00416         {
00417             found = false ;
00418             string sizestr ;
00419             TheBESKeys::TheKeys()->get_value( "BES.SockRecvSize",
00420                                               sizestr, found ) ;
00421             istringstream sizestrm( sizestr ) ;
00422             unsigned int sizenum = 0 ;
00423             sizestrm >> sizenum ;
00424             if( !sizenum )
00425             {
00426                 string err = "Socket Recv Size malformed: " + sizestr ;
00427                 throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00428             }
00429 
00430             // call setsockopt
00431             int err = setsockopt( _socket, SOL_SOCKET, SO_RCVBUF,
00432                               (char *)&sizenum, (socklen_t)sizeof(sizenum) ) ;
00433             int myerrno = errno ;
00434             if( err == -1 )
00435             {
00436                 char *serr = strerror( myerrno ) ;
00437                 string err = "Failed to set the socket receive buffer size: " ;
00438                 if( serr )
00439                     err += serr ;
00440                 else
00441                     err += "unknow error occurred" ;
00442                 throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00443             }
00444 
00445             BESDEBUG( "ppt", "Tcp receive buffer size set to "
00446                              << (unsigned long)sizenum << endl ) ;
00447         }
00448     }
00449 }
00450 
00469 void
00470 TcpSocket::setTcpSendBufferSize()
00471 {
00472     bool found = false ;
00473     vector<string> vals ;
00474     string setit ;
00475     try
00476     {
00477         TheBESKeys::TheKeys()->get_value( "BES.SetSockSendSize", setit, found );
00478     }
00479     catch( ... )
00480     {
00481         // ignore any exceptions caught trying to get this key. The
00482         // client also calls this function.
00483         setit = "No" ;
00484     }
00485     if( setit == "Yes" || setit == "yes" || setit == "Yes" )
00486     {
00487         found = false ;
00488         string sizestr ;
00489         try
00490         {
00491             TheBESKeys::TheKeys()->get_value( "BES.SockSendSize", sizestr, found ) ;
00492         }
00493         catch( BESError &e )
00494         {
00495             throw BESInternalFatalError( e.get_message(), e.get_file(),
00496                                          e.get_line() ) ;
00497         }
00498         istringstream sizestrm( sizestr ) ;
00499         unsigned int sizenum = 0 ;
00500         sizestrm >> sizenum ;
00501         if( !sizenum )
00502         {
00503             string err = "Socket Send Size malformed: " + sizestr ;
00504             throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00505         }
00506 
00507         // call setsockopt
00508         int err = setsockopt( _socket, SOL_SOCKET, SO_SNDBUF,
00509                           (char *)&sizenum, (socklen_t)sizeof(sizenum) ) ;
00510         int myerrno = errno ;
00511         if( err == -1 )
00512         {
00513             char *serr = strerror( myerrno ) ;
00514             string err = "Failed to set the socket send buffer size: " ;
00515             if( serr )
00516                 err += serr ;
00517             else
00518                 err += "unknow error occurred" ;
00519             throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00520         }
00521 
00522         BESDEBUG( "ppt", "Tcp send buffer size set to "
00523                          << (unsigned long)sizenum << endl ) ;
00524     }
00525 }
00526 
00535 unsigned int    
00536 TcpSocket::getRecvBufferSize()
00537 {
00538     if( !_haveRecvBufferSize )
00539     {
00540         // call getsockopt and set the internal variables to the result
00541         unsigned int sizenum = 0 ;
00542         socklen_t sizelen = sizeof(sizenum) ;
00543         int err = getsockopt( _socket, SOL_SOCKET, SO_RCVBUF,
00544                           (char *)&sizenum, (socklen_t *)&sizelen ) ;
00545         int myerrno = errno ;
00546         if( err == -1 )
00547         {
00548             char *serr = strerror( myerrno ) ;
00549             string err = "Failed to get the socket receive buffer size: " ;
00550             if( serr )
00551                 err += serr ;
00552             else
00553                 err += "unknow error occurred" ;
00554             throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00555         }
00556 
00557         BESDEBUG( "ppt", "Tcp receive buffer size is "
00558                          << (unsigned long)sizenum << endl ) ;
00559 
00560         _haveRecvBufferSize = true ;
00561         _recvBufferSize = sizenum ;
00562     }
00563     return _recvBufferSize ;
00564 }
00565 
00574 unsigned int
00575 TcpSocket::getSendBufferSize()
00576 {
00577     if( !_haveSendBufferSize )
00578     {
00579         // call getsockopt and set the internal variables to the result
00580         unsigned int sizenum = 0 ;
00581         socklen_t sizelen = sizeof(sizenum) ;
00582         int err = getsockopt( _socket, SOL_SOCKET, SO_SNDBUF,
00583                           (char *)&sizenum, (socklen_t *)&sizelen ) ;
00584         int myerrno = errno ;
00585         if( err == -1 )
00586         {
00587             char *serr = strerror( myerrno ) ;
00588             string err = "Failed to get the socket send buffer size: " ;
00589             if( serr )
00590                 err += serr ;
00591             else
00592                 err += "unknow error occurred" ;
00593             throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00594         }
00595 
00596         BESDEBUG( "ppt", "Tcp send buffer size is "
00597                           << (unsigned long)sizenum << endl ) ;
00598 
00599         _haveSendBufferSize = true ;
00600         _sendBufferSize = sizenum ;
00601     }
00602     return _sendBufferSize ;
00603 }
00604 
00608 bool
00609 TcpSocket::allowConnection()
00610 {
00611     bool retval = true ;
00612 
00613 #ifdef HAVE_LIBWRAP
00614     struct request_info req ;
00615     request_init( &req, RQ_DAEMON, "besdaemon", RQ_FILE,
00616                   getSocketDescriptor(), 0 ) ;
00617     fromhost() ;
00618 
00619     if( STR_EQ( eval_hostname(), paranoid ) && hosts_access() )
00620     {
00621         retval = false ;
00622     }
00623 #endif
00624 
00625     return retval ;
00626 }
00627 
00634 void
00635 TcpSocket::dump( ostream &strm ) const
00636 {
00637     strm << BESIndent::LMarg << "TcpSocket::dump - ("
00638          << (void *)this << ")" << endl ;
00639     BESIndent::Indent() ;
00640     strm << BESIndent::LMarg << "host: " << _host << endl ;
00641     strm << BESIndent::LMarg << "port: " << _portVal << endl ;
00642     strm << BESIndent::LMarg << "have recv buffer size: " << _haveRecvBufferSize
00643          << endl ;
00644     strm << BESIndent::LMarg << "recv buffer size: " << _recvBufferSize
00645          << endl ;
00646     strm << BESIndent::LMarg << "have send buffer size: " << _haveSendBufferSize
00647          << endl ;
00648     strm << BESIndent::LMarg << "send buffer size: " << _sendBufferSize
00649          << endl ;
00650     Socket::dump( strm ) ;
00651     BESIndent::UnIndent() ;
00652 }
00653