Revision a8df362ce490e6dee0c3258038d5d7c695cf39d9 authored by Unknown Author on 14 December 2006, 18:05:20 UTC, committed by Unknown Author on 14 December 2006, 18:05:20 UTC
git-svn-id: http://root.cern.ch/svn/root/tags/v5-14-00@17229 27541ba8-7e3a-0410-8455-c3a389f83636
1 parent 6ac683f
XrdProofConn.cxx
// @(#)root/proofd:$Name: $:$Id: XrdProofConn.cxx,v 1.14 2006/09/29 08:17:21 rdm Exp $
// Author: Gerardo Ganis 12/12/2005
/*************************************************************************
* Copyright (C) 1995-2005, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/
//////////////////////////////////////////////////////////////////////////
// //
// XrdProofConn //
// //
// Authors: G. Ganis, CERN, 2005 //
// //
// Low level handler of connections to xproofd. //
// //
//////////////////////////////////////////////////////////////////////////
#include "XrdProofConn.h"
#include "XProofProtocol.h"
#include "XrdClient/XrdClientConnMgr.hh"
#include "XrdClient/XrdClientConst.hh"
#include "XrdClient/XrdClientDebug.hh"
#include "XrdClient/XrdClientEnv.hh"
#include "XrdClient/XrdClientLogConnection.hh"
#include "XrdClient/XrdClientPhyConnection.hh"
#include "XrdClient/XrdClientMessage.hh"
#include "XrdClient/XrdClientUrlInfo.hh"
#include "XrdNet/XrdNetDNS.hh"
#include "XrdOuc/XrdOucPthread.hh"
#include "XrdOuc/XrdOucString.hh"
#include "XrdSec/XrdSecInterface.hh"
// Dynamic libs
// Bypass Solaris ELF madness
#if (defined(SUNCC) || defined(SUN))
#include <sys/isa_defs.h>
#if defined(_ILP32) && (_FILE_OFFSET_BITS != 32)
#undef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 32
#undef _LARGEFILE_SOURCE
#endif
#endif
#ifndef WIN32
#include <dlfcn.h>
#if !defined(__APPLE__)
#include <link.h>
#endif
#endif
// Tracing utils
#include "XrdProofdTrace.h"
extern XrdOucTrace *XrdProofdTrace;
static const char *TraceID = " ";
#define TRACEID TraceID
#ifndef WIN32
#include <sys/socket.h>
#include <sys/types.h>
#include <pwd.h>
#else
#include <process.h>
#include <Winsock2.h>
#endif
// Security handle
typedef XrdSecProtocol *(*XrdSecGetProt_t)(const char *, const struct sockaddr &,
const XrdSecParameters &, XrdOucErrInfo *);
XrdClientConnectionMgr *XrdProofConn::fgConnMgr = 0;
// Retry controllers
int XrdProofConn::fgMaxTry = 5;
int XrdProofConn::fgTimeWait = 2; // seconds
#ifndef SafeDelete
#define SafeDelete(x) { if (x) { delete x; x = 0; } }
#endif
#define URLTAG "["<<fUrl.Host<<":"<<fUrl.Port<<"]"
//_____________________________________________________________________________
XrdProofConn::XrdProofConn(const char *url, char m, int psid, char capver,
XrdClientAbsUnsolMsgHandler *uh, const char *logbuf)
: fMode(m), fConnected(0), fSessionID(psid), fLastErr(kXR_Unsupported),
fCapVer(capver), fLoginBuffer(logbuf), fMutex(0), fPhyConn(0),
fUnsolMsgHandler(uh)
{
// Constructor. Open the connection to a remote XrdProofd instance.
// The mode 'm' indicates the role of this connection:
// 'a' Administrator; used by an XPD to contact the head XPD
// 'i' Internal; used by a TXProofServ to call back its creator
// (see XrdProofUnixConn)
// 'M' Client contacting a top master
// 'm' Top master contacting a submaster
// 's' Master contacting a slave
// The buffer 'logbuf' is a null terminated string to be sent over at
// login. In case of need, internally it is overwritten with a token
// needed during redirection.
// Initialization
if (url && !Init(url)) {
if (GetServType() != kSTProofd)
TRACE(REQ, "XrdProofConn: severe error occurred while opening a"
" connection" << " to server "<<URLTAG);
return;
}
}
//_____________________________________________________________________________
void XrdProofConn::GetRetryParam(int &maxtry, int &timewait)
{
// Retrieve current values of the retry control parameters, numer of retries
// and wait time between attempts (in seconds).
maxtry = fgMaxTry;
timewait = fgTimeWait;
}
//_____________________________________________________________________________
void XrdProofConn::SetRetryParam(int maxtry, int timewait)
{
// Change values of the retry control parameters, numer of retries
// and wait time between attempts (in seconds).
fgMaxTry = maxtry;
fgTimeWait = timewait;
}
//_____________________________________________________________________________
bool XrdProofConn::Init(const char *url)
{
// Initialization
// Init connection manager (only once)
if (!fgConnMgr) {
if (!(fgConnMgr = new XrdClientConnectionMgr())) {
TRACE(REQ,"XrdProofConn::Init: error initializing connection manager");
return 0;
}
}
// Parse Url
fUrl.TakeUrl(XrdOucString(url));
fUser = fUrl.User.c_str();
// Get username from Url
if (fUser.length() <= 0) {
// If not specified, use local username
#ifndef WIN32
struct passwd *pw = getpwuid(getuid());
fUser = pw ? pw->pw_name : "";
#else
char name[256];
DWORD length = sizeof (name);
::GetUserName(name, &length);
if (strlen(name) > 1)
fUser = name;
#endif
}
fHost = fUrl.Host.c_str();
fPort = fUrl.Port;
// Max number of tries and timeout
int maxTry = (fgMaxTry > -1) ? fgMaxTry : EnvGetLong(NAME_FIRSTCONNECTMAXCNT);
int timeWait = (fgTimeWait > -1) ? fgTimeWait : EnvGetLong(NAME_CONNECTTIMEOUT);
int logid = -1;
int i = 0;
for (; (i < maxTry) && (!fConnected); i++) {
// Try connection
logid = Connect();
// We are connected to a host. Let's handshake with it.
if (fConnected) {
// Set the port used
fPort = fUrl.Port;
if (fPhyConn->IsLogged() == kNo) {
// Now the have the logical Connection ID, that we can use as streamid for
// communications with the server
TRACE(REQ,"XrdProofConn::Init: new logical connection ID: "<<logid);
// Get access to server
if (!GetAccessToSrv()) {
fConnected = 0;
if (GetServType() == kSTProofd)
return fConnected;
if (fLastErr == kXR_NotAuthorized || fLastErr == kXR_InvalidRequest) {
// Auth error or iunvalid request: does not make much sense to retry
Close("P");
XrdOucString msg = fLastErrMsg;
msg.erase(msg.rfind(":"));
TRACE(REQ,"XrdProofConn::Init: failure: " << msg);
return fConnected;
} else {
TRACE(REQ,"XrdProofConn::Init: access to server failed (" << fLastErrMsg << ")");
}
continue;
} else {
// Manager call in client: no need to create or attach: just notify
TRACE(REQ,"XrdProofConn::Init: access to server granted.");
break;
}
}
// Notify
TRACE(REQ,"XrdProofConn::Init: session create / attached successfully.");
break;
}
// We force a physical disconnection in this special case
TRACE(REQ,"XrdProofConn::Init: disconnecting.");
Close("P");
// And we wait a bit before retrying
if (i < maxTry - 1) {
TRACE(REQ,"XrdProofConn::Init: connection attempt failed: sleep " << timeWait << " secs");
sleep(timeWait);
}
} //for connect try
// We are done
return fConnected;
}
//_____________________________________________________________________________
XrdProofConn::~XrdProofConn()
{
// Destructor
// Disconnect from remote server (the connection manager is
// responsible of the underlying physical connection, so we do not
// force its closing)
Close();
}
//_____________________________________________________________________________
int XrdProofConn::Connect()
{
// Connect to remote server
int logid;
logid = -1;
// Resolve the DNS information
char *haddr[10] = {0}, *hname[10] = {0};
int naddr = XrdNetDNS::getAddrName(fUrl.Host.c_str(), 10, haddr, hname);
int i = 0;
for (; i < naddr; i++ ) {
// Address
fUrl.HostAddr = (const char *) haddr[i];
// Name
fUrl.Host = (const char *) hname[i];
// Notify
TRACE(REQ,"XrdProofConn::Connect: found host "<<fUrl.Host<<
" with addr " << fUrl.HostAddr);
}
// Set port: the first time find the default
static int servdef = -1;
if (servdef < 0) {
struct servent *ent = getservbyname("proofd", "tcp");
servdef = (ent) ? (int)ntohs(ent->s_port) : 1093;
}
fUrl.Port = (fUrl.Port <= 0) ? servdef : fUrl.Port;
// Connect
if ((logid = fgConnMgr->Connect(fUrl)) < 0) {
TRACE(REQ,"XrdProofConn::Connect: creating logical connection to " <<URLTAG);
fLogConnID = logid;
fConnected = 0;
return -1;
}
TRACE(REQ,"XrdProofConn::Connect: connect to "<<URLTAG<<" returned "<<logid );
// Set some vars
fLogConnID = logid;
fStreamid = fgConnMgr->GetConnection(fLogConnID)->Streamid();
fPhyConn = fgConnMgr->GetConnection(fLogConnID)->GetPhyConnection();
fConnected = 1;
// Handle asynchronous requests
SetAsync(fUnsolMsgHandler);
// We are done
return logid;
}
//_____________________________________________________________________________
void XrdProofConn::Close(const char *opt)
{
// Close connection. Available options are (case insensitive)
// 'P' force closing of the underlying physical connection
// 'S' shutdown remote session, is any
// A session ID can be given using #...# signature, e.g. "#1#".
// Default is opt = "".
// Cleanup mutex
SafeDelete(fMutex);
// Make sure we are connected
if (!fConnected)
return;
// Parse options
bool hard = (opt) ? (strchr(opt,'P') || strchr(opt,'p')) : 0;
// Close connection
if (fgConnMgr) {
fgConnMgr->Disconnect(GetLogConnID(), hard);
if (hard)
fgConnMgr->GarbageCollect();
}
// Flag this action
fConnected = 0;
// We are done
return;
}
//_____________________________________________________________________________
UnsolRespProcResult XrdProofConn::ProcessUnsolicitedMsg(XrdClientUnsolMsgSender *,
XrdClientMessage *)
{
// We are here if an unsolicited response comes from a logical conn
// The response comes in the form of an XrdClientMessage *, that must NOT be
// destroyed after processing. It is destroyed by the first sender.
// Remember that we are in a separate thread, since unsolicited
// responses are asynchronous by nature.
TRACE(REQ,"XrdProofConn::ProcessUnsolicitedMsg: processing unsolicited response");
return kUNSOL_KEEP;
}
//_____________________________________________________________________________
void XrdProofConn::SetAsync(XrdClientAbsUnsolMsgHandler *uh)
{
// Set handler of unsolicited responses
if (fgConnMgr)
fgConnMgr->GetConnection(fLogConnID)->UnsolicitedMsgHandler = uh;
}
//_____________________________________________________________________________
XrdClientMessage *XrdProofConn::ReadMsg()
{
// Pickup message from the queue
return (fgConnMgr ? fgConnMgr->ReadMsg(fLogConnID) : (XrdClientMessage *)0);
}
//_____________________________________________________________________________
XrdClientMessage *XrdProofConn::SendRecv(XPClientRequest *req, const void *reqData,
void **answData)
{
// SendRecv sends a command to the server and to get a response.
// The header of the last response is returned as pointer to a XrdClientMessage.
// The data, if any, are returned in *answData; if *answData == 0 in input,
// the buffer is internally allocated and must be freed by the caller.
// If (*answData != 0) the program assumes that the caller has allocated
// enough bytes to contain the reply.
if (!fMutex)
fMutex = new XrdOucRecMutex();
XrdOucMutexHelper l(*fMutex);
XrdClientMessage *xmsg = 0;
// We have to unconditionally set the streamid inside the
// header, because, in case of 'rebouncing here', the Logical Connection
// ID might have changed, while in the header to write it remained the
// same as before, not valid anymore
SetSID(req->header.streamid);
// Notify what we are going to send
if (TRACING(ALL))
XPD::smartPrintClientHeader(req);
// We need the right order
int reqDataLen = req->header.dlen;
if (XPD::clientMarshall(req) != 0) {
TRACE(REQ, "XrdProofConn::SendRecv: problems marshalling "<<URLTAG);
return xmsg;
}
if (LowWrite(req, reqData, reqDataLen) != kOK) {
TRACE(REQ, "XrdProofConn::SendRecv: problems sending request to server "<<URLTAG);
return xmsg;
}
// Check if the client has already allocated the buffer
bool needalloc = (answData && !(*answData));
// Read from server the answer
// Note that the answer can be composed by many reads, in the case that
// the status field of the responses is kXR_oksofar
size_t dataRecvSize = 0;
do {
//
// NB: Xmsg contains ALSO the information about the result of
// the communication at low level.
kXR_int16 xst = kXR_error;
if (!(xmsg = ReadMsg()) ||
xmsg->IsError()) {
TRACE(REQ, "XrdProofConn::SendRecv: reading msg from connmgr (server "<<URLTAG<<")");
} else {
// Dump header, if required
if (TRACING(ALL))
XPD::smartPrintServerHeader(&(xmsg->fHdr));
// Get the status
xst = xmsg->HeaderStatus();
}
// We save the result, if the caller wants so. In any case
// we update the counters
if ((xst == kXR_ok) || (xst == kXR_oksofar) || (xst == kXR_authmore)) {
if (answData && xmsg->DataLen() > 0) {
if (needalloc) {
*answData = realloc(*answData, dataRecvSize + xmsg->DataLen());
if (!(*answData)) {
// Memory resources exhausted
TRACE(REQ, "XrdProofConn::SendRecv: reallocating "<<dataRecvSize<<" bytes");
free(*answData);
*answData = 0;
SafeDelete(xmsg);
return xmsg;
}
}
// Now we copy the content of the Xmsg to the buffer where
// the data are needed
memcpy(((kXR_char *)(*answData))+dataRecvSize,
xmsg->GetData(), xmsg->DataLen());
//
// Dump the buffer *answData, if requested
if (TRACING(ALL)) {
TRACE(REQ, "XrdProofConn::SendRecv: dumping read data ...");
for (int jj = 0; jj < xmsg->DataLen(); jj++) {
printf("0x%.2x ", *(((kXR_char *)xmsg->GetData())+jj));
if (!(jj%10)) printf("\n");
}
}
}
// Update counters
dataRecvSize += xmsg->DataLen();
} else if (xst != kXR_error) {
//
// Status unknown: protocol error?
TRACE(REQ, "XrdProofConn::SendRecv: status in reply is unknown ["<<
XPD::convertRespStatusToChar(xmsg->fHdr.status)<<
"] (server "<<URLTAG<<") - Abort");
// We cannot continue
SafeDelete(xmsg);
return xmsg;
}
// The last message may be empty: not an error
if (xmsg && (xst == kXR_oksofar) && (xmsg->DataLen() == 0))
return xmsg;
} while (xmsg && (xmsg->HeaderStatus() == kXR_oksofar));
// We might have collected multiple partial response also in a given mem block
if (xmsg)
xmsg->fHdr.dlen = dataRecvSize;
return xmsg;
}
//_____________________________________________________________________________
XrdClientMessage *XrdProofConn::SendReq(XPClientRequest *req, const void *reqData,
void **answData, const char *CmdName)
{
// SendReq tries to send a single command for a number of times
XrdClientMessage *answMex = 0;
int retry = 0;
bool resp = 0, abortcmd = 0;
// We need the unmarshalled request for retries
XPClientRequest reqsave;
memcpy(&reqsave, req, sizeof(XPClientRequest));
while (!abortcmd && !resp) {
abortcmd = 0;
// Send the cmd, dealing automatically with redirections and
// redirections on error
TRACE(REQ,"XrdProofConn::SendReq: calling SendRecv");
answMex = SendRecv(req, reqData, answData);
// On serious communication error we retry for a number of times,
// waiting for the server to come back
retry++;
if (!answMex || answMex->IsError()) {
TRACE(REQ,"XrdProofConn::SendReq: communication error detected with "<<URLTAG);
if (retry > kXR_maxReqRetry) {
TRACE(REQ,"XrdProofConn::SendReq: max number of retries reached - Abort");
abortcmd = 1;
} else {
abortcmd = 0;
// Restore the unmarshalled request
memcpy(req, &reqsave, sizeof(XPClientRequest));
}
} else {
// We are here if we got an answer for the command, so
// the server (original or redirected) is alive
resp = CheckResp(&(answMex->fHdr), CmdName);
// If the answer was not (or not totally) positive, we must
// investigate on the result
if (!resp)
abortcmd = CheckErrorStatus(answMex, retry, CmdName);
if (retry > kXR_maxReqRetry) {
TRACE(REQ,"XrdProofConn::SendReq: max number of retries reached - Abort");
abortcmd = 1;
}
}
if (abortcmd)
// Cleanup if failed
SafeDelete(answMex);
}
// We are done
return answMex;
}
//_____________________________________________________________________________
bool XrdProofConn::CheckResp(struct ServerResponseHeader *resp, const char *method)
{
// Checks if the server's response is ours.
// If the response's status is "OK" returns 1; if the status is "redirect", it
// means that the max number of redirections has been achieved, so returns 0.
if (MatchStreamID(resp)) {
if (resp->status != kXR_ok && resp->status != kXR_authmore &&
resp->status != kXR_wait) {
TRACE(REQ,"XrdProofConn::CheckResp: server "<<URLTAG<<
" did not return OK replying to last request");
return 0;
}
return 1;
} else {
TRACE(REQ, method << " return message not belonging to this client"
" - Protocol error");
return 0;
}
}
//_____________________________________________________________________________
bool XrdProofConn::MatchStreamID(struct ServerResponseHeader *ServerResponse)
{
// Check stream ID matching
char sid[2];
memcpy(sid, &fStreamid, sizeof(sid));
// Matches the streamid contained in the server's response with the ours
return (memcmp(ServerResponse->streamid, sid, sizeof(sid)) == 0 );
}
//_____________________________________________________________________________
void XrdProofConn::SetSID(kXR_char *sid) {
// Set our stream id, to match against that one in the server's response.
memcpy((void *)sid, (const void*)&fStreamid, 2);
}
//_____________________________________________________________________________
XReqErrorType XrdProofConn::LowWrite(XPClientRequest *req, const void* reqData,
int reqDataLen)
{
// Send request to server
// (NB: req is marshalled at this point, so we need also the plain reqDataLen)
// Strong mutual exclusion over the physical channel
XrdClientPhyConnLocker pcl(fPhyConn);
int wc = 0;
//
// Send header info first
int len = sizeof(req->header);
if ((wc = WriteRaw(req, len)) != len) {
TRACE(REQ,"XrdProofConn::LowWrite: sending header to server "<<URLTAG<<
" (rc="<<wc<<")");
return kWRITE;
}
//
// Send data next, if any
if (reqDataLen > 0) {
//
if ((wc = WriteRaw(reqData, reqDataLen)) != reqDataLen) {
TRACE(REQ,"XrdProofConn::LowWrite: sending data ("<<reqDataLen<<
" bytes) to server "<<URLTAG<<" (rc="<<wc<<")");
return kWRITE;
}
}
return kOK;
}
//_____________________________________________________________________________
bool XrdProofConn::CheckErrorStatus(XrdClientMessage *mex, int &Retry,
const char *CmdName)
{
// Check error status
TRACE(REQ,"XrdProofConn::CheckErrorStatus: parsing reply from server "<<URLTAG);
if (mex->HeaderStatus() == kXR_error) {
//
// The server declared an error.
// In this case it's better to exit, unhandled error
struct ServerResponseBody_Error *body_err;
body_err = (struct ServerResponseBody_Error *)mex->GetData();
if (body_err) {
fLastErr = (XErrorCode)ntohl(body_err->errnum);
fLastErrMsg = body_err->errmsg;
// Print out the error information, as received by the server
TRACE(ALL,"XrdProofConn::CheckErrorStatus: error "<<fLastErr<<": '"<<fLastErrMsg<<"'");
}
return 1;
}
if (mex->HeaderStatus() == kXR_wait) {
//
// We have to wait for a specified number of seconds and then
// retry the same cmd
struct ServerResponseBody_Wait *body_wait;
body_wait = (struct ServerResponseBody_Wait *)mex->GetData();
if (body_wait) {
int sleeptime = ntohl(body_wait->seconds);
if (mex->DataLen() > 4) {
TRACE(REQ,"XrdProofConn::CheckErrorStatus: wait request ("<<sleeptime<<
" secs); message: "<<(const char*)body_wait->infomsg);
} else {
TRACE(REQ,"XrdProofConn::CheckErrorStatus: wait request ("<<sleeptime<<" secs)");
}
sleep(sleeptime);
}
// We don't want kxr_wait to count as an error
Retry--;
return 0;
}
// We don't understand what the server said. Better investigate on it...
TRACE(REQ,"XrdProofConn::CheckErrorStatus: after: "<<CmdName<<
": server reply not recognized - Protocol error");
return 1;
}
//_____________________________________________________________________________
bool XrdProofConn::GetAccessToSrv()
{
// Gets access to the connected server.
// The login and authorization steps are performed here.
// Now we are connected and we ask for the kind of the server
{ XrdClientPhyConnLocker pcl(fPhyConn);
fServerType = DoHandShake();
}
switch (fServerType) {
case kSTXProofd:
TRACE(REQ,"XrdProofConn::GetAccessToSrv: found server at "<<URLTAG);
// Now we can start the reader thread in the physical connection, if needed
fPhyConn->StartReader();
fPhyConn->SetTTL(DLBD_TTL);// = DLBD_TTL;
fPhyConn->fServerType = kSTBaseXrootd;
break;
case kSTProofd:
TRACE(REQ,"XrdProofConn::GetAccessToSrv: server at "<<URLTAG<<" is a proofd");
// Close correctly this connection to proofd
kXR_int32 dum[2];
dum[0] = (kXR_int32)htonl(0);
dum[1] = (kXR_int32)htonl(2034);
WriteRaw(&dum[0], sizeof(dum));
Close("P");
return 0;
case kSTError:
TRACE(REQ,"XrdProofConn::GetAccessToSrv: handShake failed with server "<<URLTAG);
Close("P");
return 0;
case kSTNone:
TRACE(REQ,"XrdProofConn::GetAccessToSrv: server at "<<URLTAG<<" is unknown");
Close("P");
return 0;
}
bool ok = (fPhyConn->IsLogged() == kNo) ? Login() : 1;
if (!ok) {
TRACE(REQ,"XrdProofConn::GetAccessToSrv: client could not login at "<<URLTAG);
return ok;
}
// We are done
return ok;
}
//_____________________________________________________________________________
int XrdProofConn::WriteRaw(const void *buf, int len)
{
// Low level write call
if (fgConnMgr)
return fgConnMgr->WriteRaw(fLogConnID, buf, len, 0);
// No connection open
return -1;
}
//_____________________________________________________________________________
int XrdProofConn::ReadRaw(void *buf, int len)
{
// Low level write call
if (fgConnMgr)
return fgConnMgr->ReadRaw(fLogConnID, buf, len);
// No connection open
return -1;
}
//_____________________________________________________________________________
XrdProofConn::ESrvType XrdProofConn::DoHandShake()
{
// Performs initial hand-shake with the server in order to understand which
// kind of server is there at the other side
// Nothing to do if already connected
if (fPhyConn->fServerType == kSTBaseXrootd) {
TRACE(REQ,"XrdProofConn::DoHandShake: already connected to a PROOF server "<<URLTAG);
return kSTXProofd;
}
// Set field in network byte order
struct ClientInitHandShake initHS;
memset(&initHS, 0, sizeof(initHS));
initHS.third = (kXR_int32)htonl((int)1);
// Send to the server the initial hand-shaking message asking for the
// kind of server
int len = sizeof(initHS);
TRACE(REQ,"XrdProofConn::DoHandShake: step 1: sending "<<len<<" bytes to server "<<URLTAG);
int writeCount = WriteRaw(&initHS, len);
if (writeCount != len) {
TRACE(ALL,"XrdProofConn::DoHandShake: sending "<<len<<" bytes to server "<<URLTAG);
return kSTError;
}
// These 8 bytes are need by 'proofd' and discarded by XPD
kXR_int32 dum[2];
dum[0] = (kXR_int32)htonl(4);
dum[1] = (kXR_int32)htonl(2012);
writeCount = WriteRaw(&dum[0], sizeof(dum));
if (writeCount != sizeof(dum)) {
TRACE(ALL,"XrdProofConn::DoHandShake: sending "<<sizeof(dum)<<
" bytes to server "<<URLTAG);
return kSTError;
}
// Read from server the first 4 bytes
ServerResponseType type;
len = sizeof(type);
TRACE(REQ,"XrdProofConn::DoHandShake: step 2: reading "<<len<<" bytes from server "<<URLTAG);
// Read returns the return value of TSocket->RecvRaw... that returns the
// return value of recv (unix low level syscall)
int readCount = ReadRaw(&type, len); // 4(2+2) bytes
if (readCount != len) {
if (readCount == (int)TXSOCK_ERR_TIMEOUT) {
TRACE(ALL,"XrdProofConn::DoHandShake: -----------------------");
TRACE(ALL,"XrdProofConn::DoHandShake: TimeOut condition reached reading from remote server.");
TRACE(ALL,"XrdProofConn::DoHandShake: This may indicate that the server is a 'proofd', version <= 12");
TRACE(ALL,"XrdProofConn::DoHandShake: Retry commenting the 'Plugin.TSlave' line in system.rootrc or adding");
TRACE(ALL,"XrdProofConn::DoHandShake: Plugin.TSlave: ^xpd TSlave Proof \"TSlave(const char *,const char"
" *,int,const char *, TProof *,ESlaveType,const char *,const char *)\"");
TRACE(ALL,"XrdProofConn::DoHandShake: to your $HOME/.rootrc .");
TRACE(ALL,"XrdProofConn::DoHandShake: -----------------------");
} else {
TRACE(ALL,"XrdProofConn::DoHandShake: reading "<<len<<" bytes from server "<<URLTAG);
}
return kSTError;
}
// to host byte order
type = ntohl(type);
// Check if the server is the eXtended proofd
if (type == 0) {
struct ServerInitHandShake xbody;
// ok
len = sizeof(xbody);
TRACE(REQ,"XrdProofConn::DoHandShake: step 3: reading "<<len<<" bytes from"
" server "<<URLTAG);
readCount = ReadRaw(&xbody, len); // 12(4+4+4) bytes
if (readCount != len) {
TRACE(ALL,"XrdProofConn::DoHandShake: reading "<<len<<" bytes from server "<<URLTAG);
return kSTError;
}
XPD::ServerInitHandShake2HostFmt(&xbody);
fRemoteProtocol = xbody.protover;
return kSTXProofd;
} else if (type == 8) {
// Standard proofd
return kSTProofd;
} else {
// We don't know the server type
TRACE(ALL,"XrdProofConn::DoHandShake: unknown server type ("<<type<<")");
return kSTNone;
}
}
//_____________________________________________________________________________
int XrdProofConn::GetLowSocket()
{
// Return the socket descriptor of the underlying connection
return (fPhyConn ? fPhyConn->GetSocket() : -1);
}
//_____________________________________________________________________________
bool XrdProofConn::Login()
{
// This method perform the loggin-in into the server just after the
// hand-shake. It also calls the Authenticate() method
XPClientRequest reqhdr;
// We fill the header struct containing the request for login
memset( &reqhdr, 0, sizeof(reqhdr));
reqhdr.login.pid = getpid();
// Fill login username
if (fUser.length() >= 0)
strcpy( (char *)reqhdr.login.username, (char *)(fUser.c_str()) );
else
strcpy( (char *)reqhdr.login.username, "????" );
// This is the place to send a token for fast authentication
// or id to the server (or any other information)
const void *buf = (const void *)(fLoginBuffer.c_str());
reqhdr.header.dlen = fLoginBuffer.length();
// Set the connection mode (see constructor header)
reqhdr.login.role[0] = fMode;
// For normal connections this is the PROOF protocol version run by the client.
// For internal connections this is the id of the session we want to be
// connected.
short int sessID = fSessionID;
// We use the 2 reserved bytes
memcpy(&reqhdr.login.reserved[0], &sessID, 2);
// Send also a capability (protocol) version number
reqhdr.login.capver[0] = (char)fCapVer;
// We call SendReq, the function devoted to sending commands.
TRACE(REQ,"XrdProofConn::Login: logging into server "<<URLTAG<<
"; pid="<<reqhdr.login.pid<<"; uid=" << reqhdr.login.username);
// Reset logged state
fPhyConn->SetLogged(kNo);
bool notdone = 1;
bool resp = 1;
// If positive answer
XrdSecProtocol *secp = 0;
while (notdone) {
// server response header
char *pltmp = 0;
SetSID(reqhdr.header.streamid);
reqhdr.header.requestid = kXP_login;
XrdClientMessage *xrsp = SendReq(&reqhdr, buf,
(void **)&pltmp, "XrdProofConn::Login");
// If positive answer
secp = 0;
char *plref = pltmp;
if (xrsp) {
//
// Pointer to data
int len = xrsp->DataLen();
if (len >= (int)sizeof(kXR_int32)) {
// The first 4 bytes contain the remote daemon version
kXR_int32 vers = 0;
memcpy(&vers, pltmp, sizeof(kXR_int32));
fRemoteProtocol = ntohl(vers);
pltmp = (char *)((char *)pltmp + sizeof(kXR_int32));
len -= sizeof(kXR_int32);
}
// Check if we need to authenticate
if (pltmp && (len > 0)) {
//
// Reset the result
resp = 0;
//
// Set some environment variables: debug
char *s = 0;
if (EnvGetLong(NAME_DEBUG) > 0) {
s = new char [strlen("XrdSecDEBUG")+20];
sprintf(s, "XrdSecDEBUG=%ld", EnvGetLong(NAME_DEBUG));
putenv(s);
}
// user name
s = new char [strlen("XrdSecUSER")+fUser.length()+2];
sprintf(s, "XrdSecUSER=%s", fUser.c_str());
putenv(s);
// host name
s = new char [strlen("XrdSecHOST")+fHost.length()+2];
sprintf(s, "XrdSecHOST=%s", fHost.c_str());
putenv(s);
// netrc file
XrdOucString netrc;
#ifndef WIN32
struct passwd *pw = getpwuid(getuid());
if (pw) {
netrc = pw->pw_dir;
netrc += "/.rootnetrc";
}
#endif
if (netrc.length() > 0) {
s = new char [strlen("XrdSecNETRC")+netrc.length()+2];
sprintf(s, "XrdSecNETRC=%s", netrc.c_str());
putenv(s);
}
//
// Null-terminate server reply
char *plist = new char[len+1];
memcpy(plist, pltmp, len);
plist[len] = 0;
TRACE(REQ,"XrdProofConn::Login: server requires authentication");
secp = Authenticate(plist, (int)(len+1));
resp = (secp != 0);
if (plist)
delete[] plist;
} else {
// We are successfully done
resp = 1;
notdone = 0;
}
// Cleanup
SafeDelete(xrsp);
} else {
// We failed but we are done with this attempt
resp = 0;
notdone = 0;
}
// Cleanup
if (plref)
free(plref);
}
// Flag success if everything went ok
if (resp) {
fPhyConn->SetLogged(kYes);
fPhyConn->SetSecProtocol(secp);
}
// We are done
return resp;
}
//_____________________________________________________________________________
XrdSecProtocol *XrdProofConn::Authenticate(char *plist, int plsiz)
{
// Negotiate authentication with the remote server. Tries in turn
// all available protocols proposed by the server (in plist),
// starting from the first.
static XrdSecGetProt_t getp = 0;
XrdSecProtocol *protocol = (XrdSecProtocol *)0;
if (!plist || plsiz <= 0)
return protocol;
TRACE(REQ,"XrdProofConn::Authenticate: host "<<URLTAG<<
" sent a list of "<<plsiz<<" bytes");
//
// Prepare host/IP information of the remote xrootd. This is required
// for the authentication.
struct sockaddr_in netaddr;
char **hosterrmsg = 0;
if (XrdNetDNS::getHostAddr((char *)fUrl.HostAddr.c_str(),
(struct sockaddr &)netaddr, hosterrmsg) <= 0) {
TRACE(REQ,"XrdProofConn::Authenticate: getHostAddr: "<< *hosterrmsg);
return protocol;
}
netaddr.sin_port = fUrl.Port;
//
// Variables for negotiation
XrdSecParameters *secToken = 0;
XrdSecCredentials *credentials = 0;
//
// Now try in turn the available protocols (first preferred)
bool resp = FALSE;
int lp = 0;
char *pp = strstr(plist+lp,"&P=");
while (pp && pp <= (plist + plsiz - 3)) {
//
// The delimitation id next protocol string or the end ...
char *pn = pp+3;
while (pn <= (plist + plsiz - 3)) {
if ((*pn) == '&')
if (!strncmp(pn+1,"P=",2)) break;
pn++;
}
pn = (pn > (plist + plsiz - 3)) ? 0 : pn;
//
// Token length
int lpar = (pn) ? ((int)(pn-pp)) : (plsiz - (int)(pp-plist));
//
// Prepare the parms object
char *bpar = (char *)malloc(lpar+1);
if (bpar)
memcpy(bpar, pp, lpar);
bpar[lpar] = 0;
XrdSecParameters Parms(bpar,lpar+1);
// We need to load the protocol getter the first time we are here
if (!getp) {
// Open the security library
void *lh = 0;
if (!(lh = dlopen("libXrdSec.so", RTLD_NOW))) {
TRACE(REQ,"XrdProofConn::Authenticate: unable to load libXrdSec.so");
return protocol;
}
// Get the client protocol getter
if (!(getp = (XrdSecGetProt_t) dlsym(lh, "XrdSecGetProtocol"))) {
TRACE(REQ,"XrdProofConn::Authenticate: unable to load XrdSecGetProtocol()");
return protocol;
}
}
//
// Retrieve the security protocol context from the xrootd server
if (!(protocol = (*getp)((char *)fUrl.Host.c_str(),
(const struct sockaddr &)netaddr, Parms, 0))) {
TRACE(REQ,"XrdProofConn::Authenticate: unable to get protocol object.");
// Set error, in case of need
fLastErr = kXR_NotAuthorized;
fLastErrMsg = "unable to get protocol object.";
pp = pn;
continue;
}
//
// Protocol name
XrdOucString protname = protocol->Entity.prot;
//
// Once we have the protocol, get the credentials
credentials = protocol->getCredentials(&Parms);
if (!credentials) {
TRACE(REQ,"XrdProofConn::Authenticate:"
" cannot obtain credentials (protocol: "<<protname<<")");
// Set error, in case of need
fLastErr = kXR_NotAuthorized;
fLastErrMsg = "cannot obtain credentials for protocol: ";
fLastErrMsg += protname;
pp = pn;
continue;
} else {
TRACE(REQ,"XrdProofConn::Authenticate:"
"credentials size: " << credentials->size);
}
//
// We fill the header struct containing the request for login
XPClientRequest reqhdr;
memset(reqhdr.auth.reserved, 0, 12);
memcpy(reqhdr.auth.credtype, protname.c_str(), protname.length());
int status = kXR_authmore;
int dlen = 0;
char *srvans = 0;
resp = FALSE;
XrdClientMessage *xrsp = 0;
while (status == kXR_authmore) {
//
// Length of the credentials buffer
SetSID(reqhdr.header.streamid);
reqhdr.header.requestid = kXP_auth;
reqhdr.header.dlen = credentials->size;
xrsp = SendReq(&reqhdr, credentials->buffer,
(void **)&srvans, "XrdProofConn::Authenticate");
SafeDelete(credentials);
status = (xrsp) ? xrsp->HeaderStatus() : kXR_error;
dlen = (xrsp) ? xrsp->DataLen() : 0;
TRACE(REQ,"XrdProofConn::Authenticate:"
"server reply: status: "<<status<<" dlen: "<<dlen);
if (xrsp && (status == kXR_authmore)) {
//
// We are required to send additional information
// First assign the security token that we have received
// at the login request
secToken = new XrdSecParameters(srvans, dlen);
//
// then get next part of the credentials
credentials = protocol->getCredentials(secToken);
SafeDelete(secToken); // nb: srvans is released here
srvans = 0;
if (!credentials) {
TRACE(REQ,"XrdProofConn::Authenticate: cannot obtain credentials");
// Set error, in case of need
fLastErr = kXR_NotAuthorized;
fLastErrMsg = "cannot obtain credentials";
break;
} else {
TRACE(REQ,"XrdProofConn::Authenticate:"
"credentials size " << credentials->size);
}
} else if (status == kXR_ok) {
// Success
resp = TRUE;
}
// Cleanup message
SafeDelete(xrsp);
}
if (!resp)
// Get next
pp = pn;
else
// We are done
break;
}
// Return the result of the negotiation
//
return protocol;
}
//_____________________________________________________________________________
void XrdProofConn::SetInterrupt()
{
// Interrupt the underlying socket
if (fPhyConn)
fPhyConn->SetInterrupt();
}
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...