Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[XrdHTTP] Implement RFC3230 for providing resource digest #769

Merged
merged 8 commits into from
Jul 26, 2018
14 changes: 14 additions & 0 deletions src/XrdHttp/XrdHttpProtocol.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2657,6 +2657,20 @@ int XrdHttpProtocol::doStat(char *fname) {
}


int XrdHttpProtocol::doChksum(const XrdOucString &fname) {
size_t length;
memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
CurrentReq.xrdreq.query.requestid = htons(kXR_query);
CurrentReq.xrdreq.query.infotype = htons(kXR_Qcksum);
memset(CurrentReq.xrdreq.query.reserved1, '\0', sizeof(CurrentReq.xrdreq.query.reserved1));
memset(CurrentReq.xrdreq.query.fhandle, '\0', sizeof(CurrentReq.xrdreq.query.fhandle));
memset(CurrentReq.xrdreq.query.reserved2, '\0', sizeof(CurrentReq.xrdreq.query.reserved2));
length = fname.length() + 1;
CurrentReq.xrdreq.query.dlen = htonl(length);

return Bridge->Run(reinterpret_cast<char *>(&CurrentReq.xrdreq), const_cast<char *>(fname.c_str()), length) ? 0 : -1;
}


static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION);

Expand Down
3 changes: 2 additions & 1 deletion src/XrdHttp/XrdHttpProtocol.hh
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ public:
/// Perform a Stat request
int doStat(char *fname);


/// Perform a checksum request
int doChksum(const XrdOucString &fname);

/// Ctor, dtors and copy ctor
XrdHttpProtocol operator =(const XrdHttpProtocol &rhs);
Expand Down
141 changes: 107 additions & 34 deletions src/XrdHttp/XrdHttpReq.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,24 +66,42 @@
#define TRACELINK prot->Link


static XrdOucString convert_digest_name(const std::string &rfc_name)
{
if (!strcasecmp(rfc_name.c_str(), "md5")) {
return "md5";
} else if (!strcasecmp(rfc_name.c_str(), "adler32")) {
return "adler32";
} else if (strcasecmp(rfc_name.c_str(), "SHA")) {
return "sha1";
} else if (strcasecmp(rfc_name.c_str(), "SHA-256")) {
return "sha256";
} else if (strcasecmp(rfc_name.c_str(), "SHA-512")) {
return "sha512";
} else if (strcasecmp(rfc_name.c_str(), "UNIXcksum")) {
return "cksum";
}
return "unknown";
}


















static bool needs_base64_padding(const std::string &rfc_name)
{
if (!strcasecmp(rfc_name.c_str(), "md5")) {
return true;
} else if (!strcasecmp(rfc_name.c_str(), "adler32")) {
return false;
} else if (strcasecmp(rfc_name.c_str(), "SHA")) {
return true;
} else if (strcasecmp(rfc_name.c_str(), "SHA-256")) {
return true;
} else if (strcasecmp(rfc_name.c_str(), "SHA-512")) {
return true;
} else if (strcasecmp(rfc_name.c_str(), "UNIXcksum")) {
return false;
}
return false;
}


void trim(std::string &str)
Expand Down Expand Up @@ -185,6 +203,9 @@ int XrdHttpReq::parseLine(char *line, int len) {
} else if (!strcmp(key, "Destination")) {
destination.assign(val, line+len-val);
trim(destination);
} else if (!strcmp(key, "Want-Digest")) {
m_req_digest.assign(val, line + len - val);
trim(m_req_digest);
} else if (!strcmp(key, "Depth")) {
depth = -1;
if (strcmp(val, "infinity"))
Expand Down Expand Up @@ -862,14 +883,31 @@ int XrdHttpReq::ProcessHTTPReq() {
}
case XrdHttpReq::rtHEAD:
{

// Do a Stat
if (prot->doStat((char *) resourceplusopaque.c_str())) {
prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0);
return -1;
if (reqstate == 0) {
// Always start with Stat; in the case of a checksum request, we'll have a follow-up query
if (prot->doStat((char *) resourceplusopaque.c_str())) {
prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0);
return -1;
}
return 0;
} else {
const char *opaque = strchr(resourceplusopaque.c_str(), '?');
// Note that doChksum requires that the memory stays alive until the callback is invoked.
m_resource_with_digest = resourceplusopaque;
if (!opaque) {
m_resource_with_digest += "?cks.type=";
m_resource_with_digest += convert_digest_name(m_req_digest);
} else {
m_resource_with_digest += "&cks.type=";
m_resource_with_digest += convert_digest_name(m_req_digest);
}
if (prot->doChksum(m_resource_with_digest) < 0) {
// In this case, the Want-Digest header was set and PostProcess gave the go-ahead to do a checksum.
prot->SendSimpleResp(500, NULL, NULL, NULL, 0);
return -1;
}
return 1;
}

return 1;
}
case XrdHttpReq::rtGET:
{
Expand Down Expand Up @@ -1469,9 +1507,11 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) {
}
case XrdHttpReq::rtHEAD:
{

if (xrdresp == kXR_ok) {

if (xrdresp != kXR_ok) {
// NOTE that HEAD MUST NOT return a body, even in the case of failure.
prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0);
return -1;
} else if (reqstate == 0) {
if (iovN > 0) {

// Now parse the stat info
Expand All @@ -1484,18 +1524,47 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) {
&fileflags,
&filemodtime);

prot->SendSimpleResp(200, NULL, NULL, NULL, filesize);
return 1;
if (m_req_digest.size()) {
return 0;
} else {
prot->SendSimpleResp(200, NULL, NULL, NULL, filesize);
return 1;
}
}

prot->SendSimpleResp(httpStatusCode, NULL, NULL,
httpStatusText.c_str(), httpStatusText.length());
prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0);
reset();
return 1;
} else {
prot->SendSimpleResp(httpStatusCode, NULL, NULL,
httpStatusText.c_str(), httpStatusText.length());
return -1;
} else { // We requested a checksum and now have its response.
if (iovN > 0) {
TRACEI(REQ, "Checksum for HEAD " << resource << " " << reinterpret_cast<char *>(iovP[0].iov_base) << "=" << reinterpret_cast<char *>(iovP[iovN-1].iov_base));

bool convert_to_base64 = needs_base64_padding(m_req_digest);
char *digest_value = reinterpret_cast<char *>(iovP[iovN-1].iov_base);
if (convert_to_base64) {
size_t digest_length = strlen(digest_value);
unsigned char *digest_binary_value = (unsigned char *)malloc(digest_length);
if (!Fromhexdigest(reinterpret_cast<unsigned char *>(digest_value), digest_length, digest_binary_value)) {
prot->SendSimpleResp(500, NULL, NULL, NULL, 0);
free(digest_binary_value);
}
char *digest_base64_value = (char *)malloc(digest_length);
Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
free(digest_binary_value);
digest_value = digest_base64_value;
}

std::string digest_response = "Digest: ";
digest_response += m_req_digest;
digest_response += "=";
digest_response += digest_value;
if (convert_to_base64) {free(digest_value);}
prot->SendSimpleResp(200, NULL, digest_response.c_str(), NULL, filesize);
return 1;
} else {
prot->SendSimpleResp(500, NULL, NULL, NULL, 0);
return -1;
}
}
}
case XrdHttpReq::rtGET:
Expand Down Expand Up @@ -2328,6 +2397,10 @@ void XrdHttpReq::reset() {
resource = "";
allheaders.clear();

// Reset the state of the request's digest request.
m_req_digest.clear();
m_resource_with_digest = "";

headerok = false;
keepalive = true;
length = 0;
Expand Down
27 changes: 13 additions & 14 deletions src/XrdHttp/XrdHttpReq.hh
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ private:

void getfhandle();


/// Cook and send the response after the bridge did something
/// Return values:
/// 0->everything OK, additionsl steps may be required
/// 1->request processed completely
/// -1->error
int PostProcessHTTPReq(bool final = false);

// Parse a resource string, typically a filename, setting the resource field and the opaque data
void parseResource(char *url);
Expand Down Expand Up @@ -202,6 +207,13 @@ public:
/// The destination field specified in the req
std::string destination;

/// The requested digest type
std::string m_req_digest;
/// The checksum algorithm is specified as part of the opaque data in the URL.
/// Hence, when a digest is generated to satisfy a request, we cache the tweaked
/// URL in this data member.
XrdOucString m_resource_with_digest;

/// Additional opaque info that may come from the hdr2cgi directive
std::string hdr2cgistr;

Expand Down Expand Up @@ -255,19 +267,6 @@ public:
/// -1->error
int ProcessHTTPReq();

/// Cook and send the response after the bridge did something
/// Return values:
/// 0->everything OK, additionsl steps may be required
/// 1->request processed completely
/// -1->error
int PostProcessHTTPReq(bool final = false);








// ------------
// Items inherited from the Bridge class
Expand Down
27 changes: 27 additions & 0 deletions src/XrdHttp/XrdHttpUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,34 @@ void Tobase64(const unsigned char *input, int length, char *out) {
}


static int
char_to_int(int c)
{
if (isdigit(c)) {
return c - '0';
} else {
c = tolower(c);
if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
return -1;
}
}


// Decode a hex digest array to raw bytes.
//
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out) {
for (int idx=0; idx < length; idx += 2) {
int upper = char_to_int(input[idx]);
int lower = char_to_int(input[idx+1]);
if ((upper < 0) || (lower < 0)) {
return false;
}
out[idx/2] = (upper << 4) + lower;
}
return true;
}


// Simple itoa function
Expand Down
4 changes: 4 additions & 0 deletions src/XrdHttp/XrdHttpUtils.hh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ int compareHash(
const char *h2);


bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out);

void Tobase64(const unsigned char *input, int length, char *out);


// Create a new quoted string
char *quote(const char *str);
Expand Down