XrdClientSock.cc

Go to the documentation of this file.
00001 //////////////////////////////////////////////////////////////////////////
00002 //                                                                      //
00003 // XrdClientSock                                                        //
00004 //                                                                      //
00005 // Author: Fabrizio Furano (INFN Padova, 2004)                          //
00006 // Adapted from TXNetFile (root.cern.ch) originally done by             //
00007 //  Alvise Dorigo, Fabrizio Furano                                      //
00008 //          INFN Padova, 2003                                           //
00009 //                                                                      //
00010 // Client Socket with timeout features using XrdNet                     //
00011 //                                                                      //
00012 //////////////////////////////////////////////////////////////////////////
00013 
00014 //         $Id: XrdClientSock.cc 30949 2009-11-02 16:37:58Z ganis $
00015 
00016 const char *XrdClientSockCVSID = "$Id: XrdClientSock.cc 30949 2009-11-02 16:37:58Z ganis $";
00017 
00018 #include <memory>
00019 #include <errno.h>
00020 #include <stdio.h>
00021 #include <string.h>
00022 #include <assert.h>
00023 #include "XrdClient/XrdClientSock.hh"
00024 #include "XrdSys/XrdSysLogger.hh"
00025 #include "XrdNet/XrdNetSocket.hh"
00026 #include "XrdNet/XrdNetOpts.hh"
00027 #include "XrdSys/XrdSysPlatform.hh"
00028 #include "XrdClient/XrdClientDebug.hh"
00029 #include "XrdClient/XrdClientEnv.hh"
00030 
00031 #ifndef WIN32
00032 #include <netinet/in.h>
00033 #include <unistd.h>
00034 #include <sys/poll.h>
00035 #else
00036 #include "XrdSys/XrdWin32.hh"
00037 #endif
00038 
00039 #ifdef __FreeBSD__
00040 #include <sys/types.h>
00041 #include <pwd.h>
00042 #endif
00043 
00044 //_____________________________________________________________________________
00045 XrdClientSock::XrdClientSock(XrdClientUrlInfo Host, int windowsize)
00046 {
00047     // Constructor
00048 
00049     fHost.TcpHost = Host;
00050     fHost.TcpWindowSize = windowsize;
00051     fConnected = FALSE;
00052     fRDInterrupt = 0;
00053     fWRInterrupt = 0;
00054     fSocket = -1;
00055     fRequestTimeout = EnvGetLong(NAME_REQUESTTIMEOUT);
00056 }
00057 
00058 //_____________________________________________________________________________
00059 XrdClientSock::~XrdClientSock()
00060 {
00061     // Destructor
00062     Disconnect();
00063 }
00064 
00065 //_____________________________________________________________________________
00066 void XrdClientSock::SetRequestTimeout(int timeout)
00067 {
00068    // Set request timeout. If timeout is non-positive reset the request
00069    // timeout to the default value
00070 
00071    fRequestTimeout = (timeout > 0) ? timeout : EnvGetLong(NAME_REQUESTTIMEOUT);
00072 }
00073 
00074 //_____________________________________________________________________________
00075 void XrdClientSock::Disconnect()
00076 {
00077     // Close the connection
00078 //     if (fConnected && fSocket >= 0) {
00079 //      ::close(fSocket);
00080 //      fConnected = FALSE;
00081 //      fSocket = -1;
00082 //     }
00083     if (fSocket >= 0) {
00084 	::close(fSocket);
00085     }
00086 
00087     fConnected = false;
00088     fSocket = -1;
00089 
00090 }
00091 
00092 //_____________________________________________________________________________
00093 int XrdClientSock::RecvRaw(void* buffer, int length, int substreamid,
00094                            int *usedsubstreamid)
00095 {
00096     // Read bytes following carefully the timeout rules
00097     struct pollfd fds_r;
00098     int bytesread = 0;
00099     int pollRet;
00100 
00101     // We cycle reading data.
00102     // An exit occurs if:
00103     // We have all the data we are waiting for
00104     // Or a timeout occurs
00105     // The connection is closed by the other peer
00106 
00107     // Init of the pollfd struct
00108     if (fSocket < 0) {
00109        Error("XrdClientSock::RecvRaw", "socket fd is " << fSocket);
00110        return TXSOCK_ERR;
00111     }
00112 
00113     fds_r.fd     = fSocket;
00114     //   fds_r.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
00115     fds_r.events = POLLIN;
00116 
00117     while (bytesread < length) {
00118 
00119         // We cycle on the poll, ignoring the possible interruptions
00120         // We are waiting for something to come from the socket
00121 
00122       // We cycle on the poll, ignoring the possible interruptions
00123       // We are waiting for something to come from the socket,
00124       // but we will not wait forever
00125       int timeleft = fRequestTimeout;
00126       do {
00127          // Wait for some event from the socket
00128          pollRet = poll(&fds_r,
00129                         1,
00130                         1000 // 1 second as a step
00131                         );
00132 
00133          if ((pollRet < 0) && (errno != EINTR) && (errno != EAGAIN) )
00134             return TXSOCK_ERR;
00135 
00136       } while (--timeleft && pollRet <= 0 && !fRDInterrupt);
00137 
00138 
00139       // If we are here, pollRet is > 0 why?
00140       //  Because the timeout and the poll error are handled inside the previous loop
00141 
00142       if (fSocket < 0) {
00143          if (fConnected) {
00144             Error("XrdClientSock::RecvRaw", "since we entered RecvRaw, socket "
00145                   "file descriptor has changed to " << fSocket);
00146             fConnected = FALSE;
00147          }
00148          return TXSOCK_ERR;
00149       }
00150 
00151       // If we have been timed-out, return a specific error code
00152       if (timeleft <= 0) {
00153          if ((DebugLevel() >= XrdClientDebug::kDUMPDEBUG))
00154             Info(XrdClientDebug::kNODEBUG,
00155                  "ClientSock::RecvRaw",
00156                  "Request timed out "<< fRequestTimeout << 
00157                  "seconds reading " << length << " bytes" <<
00158                  " from server " << fHost.TcpHost.Host <<
00159                  ":" << fHost.TcpHost.Port);
00160          return TXSOCK_ERR_TIMEOUT;
00161       }
00162 
00163       // If we have been interrupt, reset the inetrrupt and exit
00164       if (fRDInterrupt) {
00165          fRDInterrupt = 0;
00166          Error("XrdClientSock::RecvRaw", "got interrupt");
00167          return TXSOCK_ERR_INTERRUPT;
00168       }
00169 
00170 
00171       // First of all, we check if there is something to read
00172       if (fds_r.revents & (POLLIN | POLLPRI)) {
00173          int n = 0;
00174 
00175          do {
00176          n = ::recv(fSocket, static_cast<char *>(buffer) + bytesread,
00177                         length - bytesread, 0);
00178          } while(n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR));
00179 
00180          // If we read nothing, the connection has been closed by the other side
00181          if (n <= 0) {
00182             if (errno > 0) {
00183                Error("XrdClientSock::RecvRaw", "Error reading from socket: " <<
00184                                                ::strerror(errno));
00185             }
00186             return TXSOCK_ERR;
00187          }
00188          bytesread += n;
00189       }
00190 
00191       // Then we check if poll reports a complaint from the socket like disconnections
00192       if (fds_r.revents & (POLLERR | POLLHUP | POLLNVAL)) {
00193          Error("ClientSock::RecvRaw",
00194                "Disconnection detected reading " << length <<
00195                " bytes from socket " << fds_r.fd <<
00196                " (server[" << fHost.TcpHost.Host <<
00197                ":" << fHost.TcpHost.Port <<
00198                "]). Revents=" << fds_r.revents );
00199                return TXSOCK_ERR;
00200       }
00201 
00202     } // while
00203 
00204     // Return number of bytes received
00205     return bytesread;
00206 }
00207 
00208 //_____________________________________________________________________________
00209 int XrdClientSock::SendRaw_sock(const void* buffer, int length, int sock)
00210 {
00211     // Write bytes following carefully the timeout rules
00212     // (writes will not hang)
00213 
00214     struct pollfd fds_w;
00215     int byteswritten = 0;
00216     int pollRet;
00217 
00218     // Init of the pollfd structs. If sock is not valid... we can do this anyway
00219     fds_w.fd     = sock;
00220 
00221     fds_w.events = POLLOUT | POLLERR | POLLHUP | POLLNVAL;
00222 
00223     // We cycle until we write all we have to write
00224     // Or until a timeout occurs
00225 
00226     while (byteswritten < length) {
00227 
00228       // We will not wait forever
00229       int timeleft = fRequestTimeout;
00230       do {
00231          // Wait for some event from the socket
00232          pollRet = poll(&fds_w,
00233                         1,
00234                         1000 // 1 second as a step
00235                         );
00236          if (((pollRet < 0) && (errno != EINTR)) || !fConnected)
00237             return TXSOCK_ERR;
00238 
00239       } while (--timeleft && pollRet <= 0 && !fWRInterrupt);
00240 
00241       // If we have been timed-out, return a specific error code
00242       if (timeleft <= 0) { //gEnv
00243          Error("ClientSock::SendRaw",
00244                "Request timed out "<< fRequestTimeout << //gEnv
00245                "seconds writing " << length << " bytes" <<
00246                " to server " << fHost.TcpHost.Host <<
00247                ":" << fHost.TcpHost.Port);
00248 
00249          return TXSOCK_ERR_TIMEOUT;
00250       }
00251 
00252       // If we have been interrupt, reset the interrupt and exit
00253       if (fWRInterrupt) {
00254          fWRInterrupt = 0;
00255          Error("XrdClientSock::SendRaw", "got interrupt");
00256          return TXSOCK_ERR_INTERRUPT;
00257       }
00258 
00259       // First of all, we check if we are allowed to write
00260       if (fds_w.revents & POLLOUT) {
00261 
00262          // We will be retrying on errors like EAGAIN or EWOULDBLOCK,
00263          // but not forever
00264          timeleft = fRequestTimeout;
00265          int n = -1;
00266          while (n <= 0 && timeleft--) {
00267             if ((n = send(sock, static_cast<const char *>(buffer) + byteswritten,
00268                           length - byteswritten, 0)) <= 0) {
00269                if (timeleft <= 0 || (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)) {
00270                   // Real error: nothing more to do!
00271                   // If we wrote nothing, the connection has been closed by the other
00272                   Error("ClientSock::SendRaw", "Error writing to a socket: " <<
00273                   ::strerror(errno));
00274                   return (TXSOCK_ERR);
00275                } else {
00276                   // Sleep one second
00277                   sleep(1);
00278                }
00279             }
00280          }
00281          byteswritten += n;
00282       }
00283 
00284       // Then we check if poll reports a complaint from the socket like disconnections
00285       if (fds_w.revents & (POLLERR | POLLHUP | POLLNVAL)) {
00286 
00287          Error("ClientSock::SendRaw",
00288                "Disconnection detected writing " << length <<
00289                " bytes to socket " << fds_w.fd <<
00290                " (server[" << fHost.TcpHost.Host <<
00291                ":" << fHost.TcpHost.Port <<
00292                "]). Revents=" << fds_w.revents );
00293          return TXSOCK_ERR;
00294       }
00295 
00296     } // while
00297 
00298     // Return number of bytes sent
00299     return byteswritten;
00300 }
00301 
00302 //_____________________________________________________________________________
00303 int XrdClientSock::SendRaw(const void* buffer, int length, int substreamid)
00304 {
00305     // Note: here substreamid is used as "alternative socket" instead of fSocket
00306 
00307     if (substreamid > 0) 
00308        return SendRaw_sock(buffer, length, substreamid);
00309     else
00310        return SendRaw_sock(buffer, length, fSocket);
00311 }
00312 
00313 //_____________________________________________________________________________
00314 void XrdClientSock::TryConnect(bool isUnix)
00315 {
00316     // Already connected - we are done.
00317     //
00318     if (fConnected) {
00319        assert(fSocket >= 0);
00320        return;
00321     }
00322 
00323     
00324     fSocket = TryConnect_low(isUnix);
00325 
00326     if (fSocket >= 0) {
00327         
00328         // If we are using a SOCKS4 host...
00329         if ( EnvGetString(NAME_SOCKS4HOST) ) {
00330 
00331             Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect", "Handshaking with SOCKS4 host");
00332 
00333             switch (Socks4Handshake(fSocket)) {
00334 
00335             case 90:
00336                 // Everything OK!
00337                 Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect", "SOCKS4 connection OK");
00338                 break;
00339             case 91:
00340             case 92:
00341             case 93:
00342                 // Failed
00343                 Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect",
00344                      "SOCKS host refused the connection.");
00345                 Disconnect();
00346                 break;
00347             }
00348 
00349 
00350 
00351         }
00352 
00353     }
00354 }
00355 
00356 //_____________________________________________________________________________
00357 int XrdClientSock::TryConnect_low(bool isUnix, int altport, int windowsz)
00358 {
00359     int sock = -1;
00360     XrdOucString host;
00361     int port;
00362     if (!windowsz) windowsz = EnvGetLong(NAME_DFLTTCPWINDOWSIZE);
00363 
00364 
00365     host = EnvGetString(NAME_SOCKS4HOST);
00366     port = EnvGetLong(NAME_SOCKS4PORT);
00367 
00368     if (host.length() == 0) {
00369         host = fHost.TcpHost.HostAddr;
00370         port = fHost.TcpHost.Port;
00371 
00372         if (altport) port = altport;
00373     }
00374     else
00375         Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect_low", "Trying SOCKS4 host " <<
00376              host << ":" << port);
00377 
00378     std::auto_ptr<XrdNetSocket> s(new XrdNetSocket());
00379 
00380     // Log the attempt
00381     //
00382     if (!isUnix) {
00383        Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect_low",
00384             "Trying to connect to " <<
00385             fHost.TcpHost.Host << "(" << host << "):" <<
00386             port << " Windowsize=" << windowsz << " Timeout=" << EnvGetLong(NAME_CONNECTTIMEOUT));
00387     
00388        // Connect to a remote host
00389        //
00390        if (port)
00391           sock = s->Open(host.c_str(),
00392                          port, EnvGetLong(NAME_CONNECTTIMEOUT),
00393                          windowsz );
00394     } else {
00395         Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect_low",
00396              "Trying to UNIX connect to" << fHost.TcpHost.File <<
00397              "; timeout=" << EnvGetLong(NAME_CONNECTTIMEOUT));
00398 
00399         // Connect to a remote host
00400         //
00401         sock = s->Open(fHost.TcpHost.File.c_str(), -1, EnvGetLong(NAME_CONNECTTIMEOUT));
00402     }
00403 
00404     // Check if we really got a connection and the remote host is available
00405     //
00406     if (sock < 0)  {
00407         if (isUnix) {
00408             Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect_low", "Connection to" <<
00409                  fHost.TcpHost.File << " failed. (" << sock << ")");
00410         } else {
00411             Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect_low", "Connection to" <<
00412                  fHost.TcpHost.Host << ":" << fHost.TcpHost.Port << " failed. (" << sock << ")");
00413         }
00414       
00415     } else {
00416         fConnected = TRUE;
00417         int detachedFD = s->Detach();
00418         if (sock != detachedFD) {
00419             Error("ClientSock::TryConnect_low",
00420                   "Socket detach returned " << detachedFD << " but expected " << sock);
00421         }
00422     }
00423 
00424     return sock;
00425 }
00426 
00427 int XrdClientSock::Socks4Handshake(int sockid) {
00428 
00429     char buf[4096], userid[4096];
00430 #ifdef __FreeBSD__
00431     struct passwd *pwd;
00432 #endif
00433     uint16_t port;
00434     char a, b, c, d;
00435 
00436     // Issue a Connect req
00437     buf[0] = 4; // Socks version
00438     buf[1] = 1; // Connect request
00439 
00440     port = htons(fHost.TcpHost.Port);
00441     memcpy(buf+2, &port, sizeof(port)); // The port
00442 
00443     sscanf(fHost.TcpHost.HostAddr.c_str(), "%hhd.%hhd.%hhd.%hhd", &a, &b, &c, &d ); 
00444     buf[4] = a;
00445     buf[5] = b;
00446     buf[6] = c;
00447     buf[7] = d;
00448 
00449 #ifdef __FreeBSD__
00450     if ((pwd = getpwuid(geteuid())) == NULL)
00451         *userid = '\0';
00452     else
00453         (void)strncpy(userid, pwd->pw_name, L_cuserid);
00454 #else
00455     cuserid(userid);
00456 #endif
00457     strcpy(buf+8, userid);
00458 
00459     // send the buffer to the server!
00460     SendRaw(buf, 8+strlen(userid)+1, sockid);
00461 
00462     // Now wait the answer on the same sock
00463     RecvRaw(buf, 8, sockid);
00464 
00465     return buf[1];
00466 
00467 
00468 }

Generated on Tue Jul 5 14:46:18 2011 for ROOT_528-00b_version by  doxygen 1.5.1