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             setit = TheBESKeys::TheKeys()->get_key( "BES.SetSockRecvSize", found );
00407         }
00408         catch( ... )
00409         {
00410             // ignore any exceptions caught trying to get this key. The
00411             // client also calls this function.
00412             setit = "No" ;
00413         }
00414         if( setit == "Yes" || setit == "yes" || setit == "Yes" )
00415         {
00416             string sizestr
00417                 = TheBESKeys::TheKeys()->get_key( "BES.SockRecvSize", found ) ;
00418             istringstream sizestrm( sizestr ) ;
00419             unsigned int sizenum = 0 ;
00420             sizestrm >> sizenum ;
00421             if( !sizenum )
00422             {
00423                 string err = "Socket Recv Size malformed: " + sizestr ;
00424                 throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00425             }
00426 
00427             // call setsockopt
00428             int err = setsockopt( _socket, SOL_SOCKET, SO_RCVBUF,
00429                               (char *)&sizenum, (socklen_t)sizeof(sizenum) ) ;
00430             int myerrno = errno ;
00431             if( err == -1 )
00432             {
00433                 char *serr = strerror( myerrno ) ;
00434                 string err = "Failed to set the socket receive buffer size: " ;
00435                 if( serr )
00436                     err += serr ;
00437                 else
00438                     err += "unknow error occurred" ;
00439                 throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00440             }
00441 
00442             BESDEBUG( "ppt", "Tcp receive buffer size set to "
00443                              << (unsigned long)sizenum << endl )
00444         }
00445     }
00446 }
00447 
00466 void
00467 TcpSocket::setTcpSendBufferSize()
00468 {
00469     bool found = false ;
00470     string setit ;
00471     try
00472     {
00473         setit = TheBESKeys::TheKeys()->get_key( "BES.SetSockSendSize", found );
00474     }
00475     catch( ... )
00476     {
00477         // ignore any exceptions caught trying to get this key. The
00478         // client also calls this function.
00479         setit = "No" ;
00480     }
00481     if( setit == "Yes" || setit == "yes" || setit == "Yes" )
00482     {
00483         string sizestr
00484             = TheBESKeys::TheKeys()->get_key( "BES.SockSendSize", found ) ;
00485         istringstream sizestrm( sizestr ) ;
00486         unsigned int sizenum = 0 ;
00487         sizestrm >> sizenum ;
00488         if( !sizenum )
00489         {
00490             string err = "Socket Send Size malformed: " + sizestr ;
00491             throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00492         }
00493 
00494         // call setsockopt
00495         int err = setsockopt( _socket, SOL_SOCKET, SO_SNDBUF,
00496                           (char *)&sizenum, (socklen_t)sizeof(sizenum) ) ;
00497         int myerrno = errno ;
00498         if( err == -1 )
00499         {
00500             char *serr = strerror( myerrno ) ;
00501             string err = "Failed to set the socket send buffer size: " ;
00502             if( serr )
00503                 err += serr ;
00504             else
00505                 err += "unknow error occurred" ;
00506             throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00507         }
00508 
00509         BESDEBUG( "ppt", "Tcp send buffer size set to "
00510                          << (unsigned long)sizenum << endl )
00511     }
00512 }
00513 
00522 unsigned int    
00523 TcpSocket::getRecvBufferSize()
00524 {
00525     if( !_haveRecvBufferSize )
00526     {
00527         // call getsockopt and set the internal variables to the result
00528         unsigned int sizenum = 0 ;
00529         socklen_t sizelen = sizeof(sizenum) ;
00530         int err = getsockopt( _socket, SOL_SOCKET, SO_RCVBUF,
00531                           (char *)&sizenum, (socklen_t *)&sizelen ) ;
00532         int myerrno = errno ;
00533         if( err == -1 )
00534         {
00535             char *serr = strerror( myerrno ) ;
00536             string err = "Failed to get the socket receive buffer size: " ;
00537             if( serr )
00538                 err += serr ;
00539             else
00540                 err += "unknow error occurred" ;
00541             throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00542         }
00543 
00544         BESDEBUG( "ppt", "Tcp receive buffer size is "
00545                          << (unsigned long)sizenum << endl )
00546 
00547         _haveRecvBufferSize = true ;
00548         _recvBufferSize = sizenum ;
00549     }
00550     return _recvBufferSize ;
00551 }
00552 
00561 unsigned int
00562 TcpSocket::getSendBufferSize()
00563 {
00564     if( !_haveSendBufferSize )
00565     {
00566         // call getsockopt and set the internal variables to the result
00567         unsigned int sizenum = 0 ;
00568         socklen_t sizelen = sizeof(sizenum) ;
00569         int err = getsockopt( _socket, SOL_SOCKET, SO_SNDBUF,
00570                           (char *)&sizenum, (socklen_t *)&sizelen ) ;
00571         int myerrno = errno ;
00572         if( err == -1 )
00573         {
00574             char *serr = strerror( myerrno ) ;
00575             string err = "Failed to get the socket send buffer size: " ;
00576             if( serr )
00577                 err += serr ;
00578             else
00579                 err += "unknow error occurred" ;
00580             throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
00581         }
00582 
00583         BESDEBUG( "ppt", "Tcp send buffer size is "
00584                           << (unsigned long)sizenum << endl )
00585 
00586         _haveSendBufferSize = true ;
00587         _sendBufferSize = sizenum ;
00588     }
00589     return _sendBufferSize ;
00590 }
00591 
00595 bool
00596 TcpSocket::allowConnection()
00597 {
00598     bool retval = true ;
00599 
00600 #ifdef HAVE_LIBWRAP
00601     struct request_info req ;
00602     request_init( &req, RQ_DAEMON, "besdaemon", RQ_FILE,
00603                   getSocketDescriptor(), 0 ) ;
00604     fromhost() ;
00605 
00606     if( STR_EQ( eval_hostname(), paranoid ) && hosts_access() )
00607     {
00608         retval = false ;
00609     }
00610 #endif
00611 
00612     return retval ;
00613 }
00614 
00621 void
00622 TcpSocket::dump( ostream &strm ) const
00623 {
00624     strm << BESIndent::LMarg << "TcpSocket::dump - ("
00625          << (void *)this << ")" << endl ;
00626     BESIndent::Indent() ;
00627     strm << BESIndent::LMarg << "host: " << _host << endl ;
00628     strm << BESIndent::LMarg << "port: " << _portVal << endl ;
00629     strm << BESIndent::LMarg << "have recv buffer size: " << _haveRecvBufferSize
00630          << endl ;
00631     strm << BESIndent::LMarg << "recv buffer size: " << _recvBufferSize
00632          << endl ;
00633     strm << BESIndent::LMarg << "have send buffer size: " << _haveSendBufferSize
00634          << endl ;
00635     strm << BESIndent::LMarg << "send buffer size: " << _sendBufferSize
00636          << endl ;
00637     Socket::dump( strm ) ;
00638     BESIndent::UnIndent() ;
00639 }
00640 

Generated on Sat Aug 22 06:06:27 2009 for OPeNDAP Hyrax Back End Server (BES) by  doxygen 1.6.0