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,2005 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 
00041 #include <cstring>
00042 #include <cerrno>
00043 
00044 #include "TcpSocket.h"
00045 #include "SocketConfig.h"
00046 #include "BESInternalError.h"
00047 
00048 void
00049 TcpSocket::connect()
00050 {
00051     if( _listening )
00052     {
00053         string err( "Socket is already listening" ) ;
00054         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00055     }
00056 
00057     if( _connected )
00058     {
00059         string err( "Socket is already connected" ) ;
00060         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00061     }
00062 
00063     if( _host == "" )
00064         _host = "localhost" ;
00065 
00066     struct protoent *pProtoEnt ;
00067     struct sockaddr_in sin ;
00068     struct hostent *ph ;
00069     long address ;
00070     if( isdigit( _host[0] ) )
00071     {
00072         if( ( address = inet_addr( _host.c_str() ) ) == -1 )
00073         {
00074             string err( "Invalid host ip address " ) ;
00075             err += _host ;
00076             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00077         }
00078         sin.sin_addr.s_addr = address ;
00079         sin.sin_family = AF_INET ;
00080     }
00081     else
00082     {
00083         if( ( ph = gethostbyname( _host.c_str() ) ) == NULL )
00084         {
00085             switch( h_errno )
00086             {
00087                 case HOST_NOT_FOUND:
00088                     {
00089                         string err( "No such host " ) ;
00090                         err += _host ;
00091                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00092                     }
00093                 case TRY_AGAIN:
00094                     {
00095                         string err( "Host " ) ;
00096                         err += _host + " is busy, try again later" ;
00097                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00098                     }
00099                 case NO_RECOVERY:
00100                     {
00101                         string err( "DNS error for host " ) ;
00102                         err += _host ;
00103                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00104                     }
00105                 case NO_ADDRESS:
00106                     {
00107                         string err( "No IP address for host " ) ;
00108                         err += _host ;
00109                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00110                     }
00111                 default:
00112                     {
00113                         throw BESInternalError( "unknown error", __FILE__, __LINE__ ) ;
00114                     }
00115             }
00116         }
00117         else
00118         {
00119             sin.sin_family = ph->h_addrtype ;
00120             for( char **p =ph->h_addr_list; *p != NULL; p++ )
00121             {
00122                 struct in_addr in ;
00123                 (void)memcpy( &in.s_addr, *p, sizeof( in.s_addr ) ) ;
00124                 memcpy( (char*)&sin.sin_addr, (char*)&in, sizeof( in ) ) ;
00125             }
00126         }
00127     }
00128 
00129     sin.sin_port = htons( _portVal ) ;
00130     pProtoEnt = getprotobyname( "tcp" ) ;
00131     if( !pProtoEnt )
00132     {
00133         string err( "Error retreiving tcp protocol information" ) ;
00134         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00135     }
00136     
00137     _connected = false;
00138     int descript = socket( AF_INET, SOCK_STREAM, pProtoEnt->p_proto ) ;
00139     
00140     if( descript == -1 ) 
00141     {
00142         string err("getting socket descriptor: ");
00143         const char* error_info = strerror(errno);
00144         if(error_info)
00145             err += (string)error_info;
00146         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00147     } else {
00148         long holder;
00149         _socket = descript;
00150 
00151         //set socket to non-blocking mode
00152         holder = fcntl(_socket, F_GETFL, NULL);
00153         holder = holder | O_NONBLOCK;
00154         fcntl(_socket, F_SETFL, holder);
00155       
00156         int res = ::connect( descript, (struct sockaddr*)&sin, sizeof( sin ) );
00157 
00158       
00159         if( res == -1 ) 
00160         {
00161             if(errno == EINPROGRESS) {
00162           
00163                 fd_set write_fd ;
00164                 struct timeval timeout ;
00165                 int maxfd = _socket;
00166           
00167                 timeout.tv_sec = 5;
00168                 timeout.tv_usec = 0;
00169           
00170                 FD_ZERO( &write_fd);
00171                 FD_SET( _socket, &write_fd );
00172           
00173                 if( select( maxfd+1, NULL, &write_fd, NULL, &timeout) < 0 ) {
00174           
00175                     //reset socket to blocking mode
00176                     holder = fcntl(_socket, F_GETFL, NULL);
00177                     holder = holder & (~O_NONBLOCK);
00178                     fcntl(_socket, F_SETFL, holder);
00179             
00180                     //throw error - select could not resolve socket
00181                     string err( "selecting sockets: " ) ;
00182                     const char *error_info = strerror( errno ) ;
00183                     if( error_info )
00184                         err += (string)error_info ;
00185                     throw BESInternalError( err, __FILE__, __LINE__ ) ;
00186 
00187                 } 
00188                 else 
00189                 {
00190 
00191                     //check socket status
00192                     socklen_t lon;
00193                     int valopt;
00194                     lon = sizeof(int);
00195                     getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
00196             
00197                     if(valopt) 
00198                     {
00199 
00200                         //reset socket to blocking mode
00201                         holder = fcntl(_socket, F_GETFL, NULL);
00202                         holder = holder & (~O_NONBLOCK);
00203                         fcntl(_socket, F_SETFL, holder);
00204               
00205                         //throw error - did not successfully connect
00206                         string err("Did not successfully connect to server\n");
00207                         err += "Server may be down or you may be trying on the wrong port";
00208                         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00209               
00210                     } 
00211                     else 
00212                     {
00213                         //reset socket to blocking mode
00214                         holder = fcntl(_socket, F_GETFL, NULL);
00215                         holder = holder & (~O_NONBLOCK);
00216                         fcntl(_socket, F_SETFL, holder);
00217               
00218                         //succesful connetion to server
00219                         _connected = true;
00220                     }
00221                 }
00222             } 
00223             else 
00224             {
00225 
00226                 //reset socket to blocking mode
00227                 holder = fcntl(_socket, F_GETFL, NULL);
00228                 holder = holder & (~O_NONBLOCK);
00229                 fcntl(_socket, F_SETFL, holder);
00230           
00231                 //throw error - errno was not EINPROGRESS
00232                 string err("socket connect: ");
00233                 const char* error_info = strerror(errno);
00234                 if(error_info)
00235                     err += (string)error_info;
00236                 throw BESInternalError( err, __FILE__, __LINE__ ) ;
00237             }
00238         }
00239         else
00240         {
00241             // The socket connect request completed immediately
00242             // even that the socket was in non-blocking mode
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             _connected = true;
00249         }
00250         
00251     }
00252 }
00253 
00254 void
00255 TcpSocket::listen()
00256 {
00257     if( _connected )
00258     {
00259         string err( "Socket is already connected" ) ;
00260         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00261     }
00262 
00263     if( _listening )
00264     {
00265         string err( "Socket is already listening" ) ;
00266         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00267     }
00268 
00269     int on = 1 ;
00270     struct sockaddr_in server ;
00271     server.sin_family = AF_INET ;
00272     server.sin_addr.s_addr = INADDR_ANY ;
00273     struct servent *sir = 0 ;
00274     sir = getservbyport( _portVal, "tcp" ) ;
00275     if( sir )
00276     {
00277         string error = sir->s_name + (string)" is using my socket" ;
00278         throw BESInternalError( error, __FILE__, __LINE__ ) ;
00279     }
00280     server.sin_port = htons( _portVal ) ;
00281     _socket = socket( AF_INET, SOCK_STREAM, 0 ) ;
00282     if( _socket != -1 )
00283     {
00284         if( !setsockopt( _socket, SOL_SOCKET, SO_REUSEADDR,
00285                          (char*)&on, sizeof( on ) ) )
00286         {
00287             if( bind( _socket, (struct sockaddr*)&server, sizeof server) != -1 )
00288             {
00289                 int length = sizeof( server ) ;
00290 #ifdef _GETSOCKNAME_USES_SOCKLEN_T      
00291                 if( getsockname( _socket, (struct sockaddr *)&server,
00292                                  (socklen_t *)&length ) == -1 )
00293 #else
00294                     if( getsockname( _socket, (struct sockaddr *)&server,
00295                                      &length ) == -1 )
00296 #endif
00297                     {
00298                         string error( "getting socket name" ) ;
00299                         const char* error_info = strerror( errno ) ;
00300                         if( error_info )
00301                             error += " " + (string)error_info ;
00302                         throw BESInternalError( error, __FILE__, __LINE__ ) ;
00303                     }
00304                 if( ::listen( _socket, 5 ) == 0 )
00305                 {
00306                     _listening = true ;
00307                 }
00308                 else
00309                 {
00310                     string error( "could not listen TCP socket" ) ;
00311                     const char* error_info = strerror( errno ) ;
00312                     if( error_info )
00313                         error += " " + (string)error_info ;
00314                     throw BESInternalError( error, __FILE__, __LINE__ ) ;
00315                 }
00316             }
00317             else
00318             {
00319                 string error( "could not bind TCP socket" ) ;
00320                 const char* error_info = strerror( errno ) ;
00321                 if( error_info )
00322                     error += " " + (string)error_info ;
00323                 throw BESInternalError( error, __FILE__, __LINE__ ) ;
00324             }
00325         }
00326         else
00327         {
00328             string error( "could not set SO_REUSEADDR on TCP socket" ) ;
00329             const char* error_info = strerror( errno ) ;
00330             if( error_info )
00331                 error += " " + (string)error_info ;
00332             throw BESInternalError( error, __FILE__, __LINE__ ) ;
00333         }
00334     }
00335     else
00336     {
00337         string error( "could not create socket" ) ;
00338         const char *error_info = strerror( errno ) ;
00339         if( error_info )
00340             error += " " + (string)error_info ;
00341         throw BESInternalError( error, __FILE__, __LINE__ ) ;
00342     }
00343 }
00344 
00351 void
00352 TcpSocket::dump( ostream &strm ) const
00353 {
00354     strm << BESIndent::LMarg << "TcpSocket::dump - ("
00355          << (void *)this << ")" << endl ;
00356     BESIndent::Indent() ;
00357     strm << BESIndent::LMarg << "host: " << _host << endl ;
00358     strm << BESIndent::LMarg << "port: " << _portVal << endl ;
00359     Socket::dump( strm ) ;
00360     BESIndent::UnIndent() ;
00361 }
00362 

Generated on Tue Mar 4 23:13:37 2008 for OPeNDAP Back End Server (BES) by  doxygen 1.5.1