00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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
00062 Disconnect();
00063 }
00064
00065
00066 void XrdClientSock::SetRequestTimeout(int timeout)
00067 {
00068
00069
00070
00071 fRequestTimeout = (timeout > 0) ? timeout : EnvGetLong(NAME_REQUESTTIMEOUT);
00072 }
00073
00074
00075 void XrdClientSock::Disconnect()
00076 {
00077
00078
00079
00080
00081
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
00097 struct pollfd fds_r;
00098 int bytesread = 0;
00099 int pollRet;
00100
00101
00102
00103
00104
00105
00106
00107
00108 if (fSocket < 0) {
00109 Error("XrdClientSock::RecvRaw", "socket fd is " << fSocket);
00110 return TXSOCK_ERR;
00111 }
00112
00113 fds_r.fd = fSocket;
00114
00115 fds_r.events = POLLIN;
00116
00117 while (bytesread < length) {
00118
00119
00120
00121
00122
00123
00124
00125 int timeleft = fRequestTimeout;
00126 do {
00127
00128 pollRet = poll(&fds_r,
00129 1,
00130 1000
00131 );
00132
00133 if ((pollRet < 0) && (errno != EINTR) && (errno != EAGAIN) )
00134 return TXSOCK_ERR;
00135
00136 } while (--timeleft && pollRet <= 0 && !fRDInterrupt);
00137
00138
00139
00140
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
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
00164 if (fRDInterrupt) {
00165 fRDInterrupt = 0;
00166 Error("XrdClientSock::RecvRaw", "got interrupt");
00167 return TXSOCK_ERR_INTERRUPT;
00168 }
00169
00170
00171
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
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
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 }
00203
00204
00205 return bytesread;
00206 }
00207
00208
00209 int XrdClientSock::SendRaw_sock(const void* buffer, int length, int sock)
00210 {
00211
00212
00213
00214 struct pollfd fds_w;
00215 int byteswritten = 0;
00216 int pollRet;
00217
00218
00219 fds_w.fd = sock;
00220
00221 fds_w.events = POLLOUT | POLLERR | POLLHUP | POLLNVAL;
00222
00223
00224
00225
00226 while (byteswritten < length) {
00227
00228
00229 int timeleft = fRequestTimeout;
00230 do {
00231
00232 pollRet = poll(&fds_w,
00233 1,
00234 1000
00235 );
00236 if (((pollRet < 0) && (errno != EINTR)) || !fConnected)
00237 return TXSOCK_ERR;
00238
00239 } while (--timeleft && pollRet <= 0 && !fWRInterrupt);
00240
00241
00242 if (timeleft <= 0) {
00243 Error("ClientSock::SendRaw",
00244 "Request timed out "<< fRequestTimeout <<
00245 "seconds writing " << length << " bytes" <<
00246 " to server " << fHost.TcpHost.Host <<
00247 ":" << fHost.TcpHost.Port);
00248
00249 return TXSOCK_ERR_TIMEOUT;
00250 }
00251
00252
00253 if (fWRInterrupt) {
00254 fWRInterrupt = 0;
00255 Error("XrdClientSock::SendRaw", "got interrupt");
00256 return TXSOCK_ERR_INTERRUPT;
00257 }
00258
00259
00260 if (fds_w.revents & POLLOUT) {
00261
00262
00263
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
00271
00272 Error("ClientSock::SendRaw", "Error writing to a socket: " <<
00273 ::strerror(errno));
00274 return (TXSOCK_ERR);
00275 } else {
00276
00277 sleep(1);
00278 }
00279 }
00280 }
00281 byteswritten += n;
00282 }
00283
00284
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 }
00297
00298
00299 return byteswritten;
00300 }
00301
00302
00303 int XrdClientSock::SendRaw(const void* buffer, int length, int substreamid)
00304 {
00305
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
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
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
00337 Info(XrdClientDebug::kHIDEBUG, "ClientSock::TryConnect", "SOCKS4 connection OK");
00338 break;
00339 case 91:
00340 case 92:
00341 case 93:
00342
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
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
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
00400
00401 sock = s->Open(fHost.TcpHost.File.c_str(), -1, EnvGetLong(NAME_CONNECTTIMEOUT));
00402 }
00403
00404
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
00437 buf[0] = 4;
00438 buf[1] = 1;
00439
00440 port = htons(fHost.TcpHost.Port);
00441 memcpy(buf+2, &port, sizeof(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
00460 SendRaw(buf, 8+strlen(userid)+1, sockid);
00461
00462
00463 RecvRaw(buf, 8, sockid);
00464
00465 return buf[1];
00466
00467
00468 }