From 5e304bc8942769eb4dd88ca6de8af28cd6255101 Mon Sep 17 00:00:00 2001 From: Fabrizio Furano Date: Fri, 2 Jun 2017 16:01:26 +0200 Subject: [PATCH 1/2] Make XrdHTTP able to forward HTTP requests to an external, optional plugin (conceptually similar to CGI). Various stability fixes and improvements. --- src/XrdHeaders.cmake | 1 + src/XrdHttp.cmake | 1 + src/XrdHttp/XrdHttpExtHandler.cc | 68 ++++++ src/XrdHttp/XrdHttpExtHandler.hh | 142 ++++++++++++ src/XrdHttp/XrdHttpProtocol.cc | 252 +++++++++++++++++----- src/XrdHttp/XrdHttpProtocol.hh | 38 ++-- src/XrdHttp/XrdHttpReq.cc | 360 +++++++++++++++++++++---------- src/XrdHttp/XrdHttpReq.hh | 22 +- src/XrdHttp/XrdHttpUtils.cc | 26 ++- 9 files changed, 723 insertions(+), 187 deletions(-) create mode 100644 src/XrdHttp/XrdHttpExtHandler.cc create mode 100644 src/XrdHttp/XrdHttpExtHandler.hh diff --git a/src/XrdHeaders.cmake b/src/XrdHeaders.cmake index 709f3180b6e..1275195557e 100644 --- a/src/XrdHeaders.cmake +++ b/src/XrdHeaders.cmake @@ -104,6 +104,7 @@ set( XROOTD_PUBLIC_HEADERS XrdXrootd/XrdXrootdMonData.hh XrdXrootd/XrdXrootdBridge.hh XrdHttp/XrdHttpSecXtractor.hh + XrdHttp/XrdHttpExtHandler.hh ) set( XROOTD_PRIVATE_HEADERS diff --git a/src/XrdHttp.cmake b/src/XrdHttp.cmake index 194df48f258..e9f2befb220 100644 --- a/src/XrdHttp.cmake +++ b/src/XrdHttp.cmake @@ -21,6 +21,7 @@ if( BUILD_HTTP ) XrdHttp/XrdHttpProtocol.cc XrdHttp/XrdHttpProtocol.hh XrdHttp/XrdHttpReq.cc XrdHttp/XrdHttpReq.hh XrdHttp/XrdHttpSecXtractor.hh + XrdHttp/XrdHttpExtHandler.cc XrdHttp/XrdHttpExtHandler.hh XrdHttp/XrdHttpStatic.hh XrdHttp/XrdHttpTrace.cc XrdHttp/XrdHttpTrace.hh XrdHttp/XrdHttpUtils.cc XrdHttp/XrdHttpUtils.hh ) diff --git a/src/XrdHttp/XrdHttpExtHandler.cc b/src/XrdHttp/XrdHttpExtHandler.cc new file mode 100644 index 00000000000..d4497077eb7 --- /dev/null +++ b/src/XrdHttp/XrdHttpExtHandler.cc @@ -0,0 +1,68 @@ +//------------------------------------------------------------------------------ +// This file is part of XrdHTTP: A pragmatic implementation of the +// HTTP/WebDAV protocol for the Xrootd framework +// +// Copyright (c) 2017 by European Organization for Nuclear Research (CERN) +// Author: Fabrizio Furano +// File Date: May 2017 +//------------------------------------------------------------------------------ +// XRootD is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// XRootD is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with XRootD. If not, see . +//------------------------------------------------------------------------------ + + + +#include "XrdHttpExtHandler.hh" +#include "XrdHttpReq.hh" +#include "XrdHttpProtocol.hh" + +/// Sends a basic response. If the length is < 0 then it is calculated internally +int XrdHttpExtReq::SendSimpleResp(int code, char* desc, char* header_to_add, char* body, long long int bodylen) +{ + if (!prot) return -1; + + return prot->SendSimpleResp(code, desc, header_to_add, body, bodylen); +} + +int XrdHttpExtReq::BuffgetData(int blen, char **data, bool wait) { + + if (!prot) return -1; + int nb = prot->BuffgetData(blen, data, wait); + + return nb; +} + + +XrdHttpExtReq::XrdHttpExtReq(XrdHttpReq *req, XrdHttpProtocol *pr): prot(pr), +verb(req->requestverb), headers(req->allheaders) { + // Here we fill the request summary with all the fields we can + resource = req->resource.c_str(); + + // These fields usually identify the client that connected + + + if (prot->SecEntity.moninfo) { + clientdn = prot->SecEntity.moninfo; + trim(clientdn); + } + if (prot->SecEntity.host) { + clienthost = prot->SecEntity.host; + trim(clienthost); + } + if (prot->SecEntity.vorg) { + clientgroups = prot->SecEntity.vorg; + trim(clientgroups); + } + + length = req->length; +} diff --git a/src/XrdHttp/XrdHttpExtHandler.hh b/src/XrdHttp/XrdHttpExtHandler.hh new file mode 100644 index 00000000000..22951c94e76 --- /dev/null +++ b/src/XrdHttp/XrdHttpExtHandler.hh @@ -0,0 +1,142 @@ +//------------------------------------------------------------------------------ +// This file is part of XrdHTTP: A pragmatic implementation of the +// HTTP/WebDAV protocol for the Xrootd framework +// +// Copyright (c) 2017 by European Organization for Nuclear Research (CERN) +// Author: Fabrizio Furano +// File Date: May 2017 +//------------------------------------------------------------------------------ +// XRootD is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// XRootD is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with XRootD. If not, see . +//------------------------------------------------------------------------------ + + + + + + + + + + + +#ifndef __XRDHTTPEXTHANDLER_H__ +#define __XRDHTTPEXTHANDLER_H__ + +#include +#include + +class XrdLink; +class XrdSecEntity; +class XrdHttpReq; +class XrdHttpProtocol; + +// This class summarizes the content of a request, for consumption by an external plugin +class XrdHttpExtReq { +private: + XrdHttpProtocol *prot; + +public: + XrdHttpExtReq(XrdHttpReq *req, XrdHttpProtocol *pr); + + std::string verb, resource; + std::map &headers; + + std::string clientdn, clienthost, clientgroups; + long long length; + + /// Get a pointer to data read from the client, valid for up to blen bytes from the buffer. Returns the validity + int BuffgetData(int blen, char **data, bool wait); + + /// Sends a basic response. If the length is < 0 then it is calculated internally + int SendSimpleResp(int code, char *desc, char *header_to_add, char *body, long long bodylen); +}; + + +/// Base class for a plugin that can handle requests for urls that match +/// a certain set of prefixes +class XrdHttpExtHandler { + +public: + + /// Tells if the incoming path is recognized as one of the paths that have to be processed + // e.g. applying a prefix matching scheme or whatever + virtual bool MatchesPath(const char *path) = 0; + + /// Process an HTTP request and send the response using the calling + /// XrdHttpProtocol instance directly + /// Returns 0 if ok, non0 if errors + virtual int ProcessReq(XrdHttpExtReq &) = 0; + + /// Initializes the external request handler + virtual int Init(const char *cfgfile) = 0; + + //------------------------------------------------------------------------------ + //! Constructor + //------------------------------------------------------------------------------ + + XrdHttpExtHandler() {} + + //------------------------------------------------------------------------------ + //! Destructor + //------------------------------------------------------------------------------ + + virtual ~XrdHttpExtHandler() {} +}; + +/******************************************************************************/ +/* X r d H t t p G e t E x t H a n d l e r */ +/******************************************************************************/ + +//------------------------------------------------------------------------------ +//! Obtain an instance of the XrdHttpExtHandler object. +//! +//! This extern "C" function is called when a shared library plug-in containing +//! implementation of this class is loaded. It must exist in the shared library +//! and must be thread-safe. +//! +//! @param eDest -> The error object that must be used to print any errors or +//! other messages (see XrdSysError.hh). +//! @param confg -> Name of the configuration file that was used. This pointer +//! may be null though that would be impossible. +//! @param parms -> Argument string specified on the namelib directive. It may +//! be null or point to a null string if no parms exist. +//! +//! @return Success: A pointer to an instance of the XrdHttpSecXtractor object. +//! Failure: A null pointer which causes initialization to fail. +//! + +//------------------------------------------------------------------------------ + +class XrdSysError; + +#define XrdHttpExtHandlerArgs XrdSysError *eDest, \ + const char *confg, \ + const char *parms + +extern "C" XrdHttpExtHandler *XrdHttpGetExtHandler(XrdHttpExtHandlerArgs); + +//------------------------------------------------------------------------------ +//! Declare compilation version. +//! +//! Additionally, you *should* declare the xrootd version you used to compile +//! your plug-in. While not currently required, it is highly recommended to +//! avoid execution issues should the class definition change. Declare it as: +//------------------------------------------------------------------------------ + +/*! #include "XrdVersion.hh" + * XrdVERSIONINFO(XrdHttpGetExtHandler,); + * + * where is a 1- to 15-character unquoted name identifying your plugin. + */ +#endif diff --git a/src/XrdHttp/XrdHttpProtocol.cc b/src/XrdHttp/XrdHttpProtocol.cc index 4c8ec6e6c04..8935dcf3143 100644 --- a/src/XrdHttp/XrdHttpProtocol.cc +++ b/src/XrdHttp/XrdHttpProtocol.cc @@ -39,7 +39,7 @@ #include #include "XrdHttpUtils.hh" #include "XrdHttpSecXtractor.hh" - +#include "XrdHttpExtHandler.hh" #include #include @@ -55,7 +55,8 @@ /* G l o b a l s */ /******************************************************************************/ - +// It seems that eos needs this to be present +const char *XrdHttpSecEntityTident = "http"; // // Static stuff @@ -65,7 +66,6 @@ int XrdHttpProtocol::hailWait = 0; int XrdHttpProtocol::readWait = 0; int XrdHttpProtocol::Port = 1094; char *XrdHttpProtocol::Port_str = 0; -char *XrdHttpProtocol::Addr_str = 0; int XrdHttpProtocol::Window = 0; //XrdXrootdStats *XrdHttpProtocol::SI = 0; @@ -92,6 +92,7 @@ SSL_CTX *XrdHttpProtocol::sslctx = 0; BIO *XrdHttpProtocol::sslbio_err = 0; XrdCryptoFactory *XrdHttpProtocol::myCryptoFactory = 0; XrdHttpSecXtractor *XrdHttpProtocol::secxtractor = 0; +XrdHttpExtHandler *XrdHttpProtocol::exthandler = 0; static const unsigned char *s_server_session_id_context = (const unsigned char *) "XrdHTTPSessionCtx"; static int s_server_session_id_context_len = 18; @@ -179,6 +180,7 @@ XrdHttpProtocol::XrdHttpProtocol(bool imhttps) : XrdProtocol("HTTP protocol handler"), ProtLink(this), SecEntity(""), CurrentReq(this) { myBuff = 0; + Addr_str = 0; Reset(); ishttps = imhttps; @@ -304,7 +306,8 @@ int XrdHttpProtocol::GetVOMSData(XrdLink *lp) { // Add the original DN to the moninfo. Not sure if it makes sense to parametrize this or not. if (SecEntity.moninfo) free(SecEntity.moninfo); SecEntity.moninfo = X509_NAME_oneline(X509_get_subject_name(peer_cert), NULL, 0); - + TRACEI(DEBUG, " Subject name is : '" << SecEntity.moninfo << "'"); + // Here we have the user DN, and try to extract an useful user name from it if (SecEntity.name) free(SecEntity.name); SecEntity.name = 0; @@ -539,8 +542,10 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here } - if (CurrentReq.request == CurrentReq.rtUnknown) + if (CurrentReq.request == CurrentReq.rtUnknown) { + TRACE(DEBUG, " Parsing first line: " << tmpline); CurrentReq.parseFirstLine((char *)tmpline.c_str(), rc); + } else CurrentReq.parseLine((char *)tmpline.c_str(), rc); @@ -558,7 +563,10 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here } // If we are in self-redirect mode, then let's do it - if (ishttps && ssldone && selfhttps2http) { + // Do selfredirect only with 'simple' requests, otherwise poor clients may misbehave + if (ishttps && ssldone && selfhttps2http && + ( (CurrentReq.request == XrdHttpReq::rtGET) || (CurrentReq.request == XrdHttpReq::rtPUT) || + (CurrentReq.request == XrdHttpReq::rtPROPFIND)) ) { char hash[512]; time_t timenow = time(0); @@ -582,16 +590,55 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here XrdOucString dest = "Location: http://"; // Here I should put the IP addr of the server - Link->Host(); - - dest += Addr_str; - dest += ":"; - dest += Port_str; - dest += CurrentReq.resource.c_str(); - CurrentReq.appendOpaque(dest, &SecEntity, hash, timenow); - SendSimpleResp(302, NULL, (char *) dest.c_str(), 0, 0); - CurrentReq.reset(); - return 1; + + // We have to recompute it here because we don't know to which + // interface the client had connected to + struct sockaddr_storage sa; + socklen_t sl = sizeof(sa); + getsockname(this->Link->AddrInfo()->SockFD(), (struct sockaddr*)&sa, &sl); + + // now get it back and print it + char buf[256]; + bool ok = false; + + switch (sa.ss_family) { + case AF_INET: + if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) { + if (Addr_str) free(Addr_str); + Addr_str = strdup(buf); + ok = true; + } + break; + case AF_INET6: + if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) { + if (Addr_str) free(Addr_str); + Addr_str = (char *)malloc(strlen(buf)+3); + strcpy(Addr_str, "["); + strcat(Addr_str, buf); + strcat(Addr_str, "]"); + ok = true; + } + break; + default: + TRACEI(REQ, " Can't recognize the address family of the local host."); + } + + if (ok) { + dest += Addr_str; + dest += ":"; + dest += Port_str; + dest += CurrentReq.resource.c_str(); + TRACEI(REQ, " rc:" << rc << " self-redirecting to http with security token: '" << dest << "'"); + + + CurrentReq.appendOpaque(dest, &SecEntity, hash, timenow); + SendSimpleResp(302, NULL, (char *) dest.c_str(), 0, 0); + CurrentReq.reset(); + return -1; + } + + TRACEI(REQ, " rc:" << rc << " Can't perform self-redirection."); + } } @@ -622,18 +669,31 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here nfo = CurrentReq.opaque->Get("xrdhttpvorg"); if (nfo) { - TRACEI(REQ, " Setting vorg: " << nfo); + TRACEI(DEBUG, " Setting vorg: " << nfo); SecEntity.vorg = strdup(nfo); + TRACEI(REQ, " Setting vorg: " << SecEntity.vorg); } nfo = CurrentReq.opaque->Get("xrdhttpname"); if (nfo) { - TRACEI(REQ, " Setting name: " << nfo); + TRACEI(DEBUG, " Setting name: " << nfo); SecEntity.name = unquote(nfo); + TRACEI(REQ, " Setting name: " << SecEntity.name); + } + + nfo = CurrentReq.opaque->Get("xrdhttphost"); + if (nfo) { + TRACEI(DEBUG, " Setting host: " << nfo); + SecEntity.host = unquote(nfo); + TRACEI(REQ, " Setting host: " << SecEntity.host); + } + + nfo = CurrentReq.opaque->Get("xrdhttpdn"); + if (nfo) { + TRACEI(DEBUG, " Setting dn: " << nfo); + SecEntity.moninfo = unquote(nfo); + TRACEI(REQ, " Setting dn: " << SecEntity.moninfo); } - - //nfo = CurrentReq.opaque->Get("xrdhttphost"); - // TODO: compare the xrdhttphost with the real client IP // If they are different then reject @@ -646,7 +706,7 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here secretkey); if (compareHash(hash, tk)) { - TRACEI(REQ, " Invalid tk. Authentication failed."); + TRACEI(REQ, " Invalid tk '" << tk << "' != '" << hash << "'(calculated). Authentication failed."); return -1; } @@ -672,26 +732,30 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here // Now we have everything that is needed to try the login - if (!Bridge) { - if (SecEntity.name) - Bridge = XrdXrootd::Bridge::Login(&CurrentReq, Link, &SecEntity, SecEntity.name, "XrdHttp"); - else - Bridge = XrdXrootd::Bridge::Login(&CurrentReq, Link, &SecEntity, "unknown", "XrdHttp"); - + // Remember that if there is an exthandler then it has the responsibility + // for authorization in the paths that it manages + if (!exthandler || !exthandler->MatchesPath(CurrentReq.resource.c_str())) { if (!Bridge) { - TRACEI(REQ, " Autorization failed."); - return -1; + if (SecEntity.name) + Bridge = XrdXrootd::Bridge::Login(&CurrentReq, Link, &SecEntity, SecEntity.name, "XrdHttp"); + else + Bridge = XrdXrootd::Bridge::Login(&CurrentReq, Link, &SecEntity, "unknown", "XrdHttp"); + + if (!Bridge) { + TRACEI(REQ, " Autorization failed."); + return -1; + } + + // Let the bridge process the login, and then reinvoke us + DoingLogin = true; + return 0; } - - // Let the bridge process the login, and then reinvoke us - DoingLogin = true; - return 0; } // Compute and send the response. This may involve further reading from the socket rc = CurrentReq.ProcessHTTPReq(); if (rc < 0) - CurrentReq.reset(); + CurrentReq.reset(); @@ -793,6 +857,7 @@ int XrdHttpProtocol::Config(const char *ConfigFN) { else if TS_Xeq("secretkey", xsecretkey); else if TS_Xeq("desthttps", xdesthttps); else if TS_Xeq("secxtractor", xsecxtractor); + else if TS_Xeq("exthandler", xexthandler); else if TS_Xeq("selfhttps2http", xselfhttps2http); else if TS_Xeq("embeddedstatic", xembeddedstatic); else if TS_Xeq("listingredir", xlistredir); @@ -1077,6 +1142,8 @@ void XrdHttpProtocol::BuffConsume(int blen) { int XrdHttpProtocol::BuffgetData(int blen, char **data, bool wait) { int rlen; + TRACE(DEBUG, "BuffgetData: requested " << blen << " bytes"); + if (wait && (blen > BuffUsed())) { TRACE(REQ, "BuffgetData: need to read " << blen - BuffUsed() << " bytes"); if (getDataOneShot(blen - BuffUsed(), true) < 0) return 0; @@ -1123,7 +1190,7 @@ int XrdHttpProtocol::SendData(char *body, int bodylen) { /// Returns 0 if OK int XrdHttpProtocol::SendSimpleResp(int code, char *desc, char *header_to_add, char *body, long long bodylen) { - char outhdr[512]; + char outhdr[1024]; char b[32]; long long l; const char *crlf = "\r\n"; @@ -1167,12 +1234,16 @@ int XrdHttpProtocol::SendSimpleResp(int code, char *desc, char *header_to_add, c } strncat(outhdr, crlf, 2); + unsigned int hdrlen = strlen(outhdr); + if (hdrlen >= sizeof(outhdr)) + TRACEI(ALL, "WARNING: header size too large!"); + // // Send the header // TRACEI(RSP, "Sending resp: " << code << " len:" << l); - if (SendData(outhdr, strlen(outhdr))) + if (SendData(outhdr, hdrlen)) return -1; // @@ -1214,11 +1285,6 @@ int XrdHttpProtocol::Configure(char *parms, XrdProtocol_Config * pi) { char buf[16]; sprintf(buf, "%d", Port); Port_str = strdup(buf); - - - // now get it back and print it - inet_ntop(AF_INET, &((struct sockaddr_in *) pi->myAddr)->sin_addr, buf, INET_ADDRSTRLEN); - Addr_str = strdup(buf); } @@ -1325,7 +1391,13 @@ extern "C" int verify_callback(int ok, X509_STORE_CTX * store) { /// Initialization of the ssl security int XrdHttpProtocol::InitSecurity() { - + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_digests(); + #ifdef HAVE_XRDCRYPTO #ifndef WIN32 // Borrow the initialization of XrdCryptossl, in order to share the @@ -1338,11 +1410,6 @@ int XrdHttpProtocol::InitSecurity() { #endif #endif - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_ciphers(); - OpenSSL_add_all_algorithms(); - OpenSSL_add_all_digests(); const SSL_METHOD *meth; @@ -1473,7 +1540,8 @@ void XrdHttpProtocol::Cleanup() { memset(&SecEntity, 0, sizeof (SecEntity)); - + if (Addr_str) free(Addr_str); + Addr_str = 0; } void XrdHttpProtocol::Reset() { @@ -1507,7 +1575,7 @@ void XrdHttpProtocol::Reset() { // totReadP = 0; memset(&SecEntity, 0, sizeof (SecEntity)); - + SecEntity.tident = XrdHttpSecEntityTident; ishttps = false; ssldone = false; @@ -2100,6 +2168,62 @@ int XrdHttpProtocol::xsecxtractor(XrdOucStream & Config) { return 0; } + + + + + + + + +/******************************************************************************/ +/* x e x t h a n d l e r */ +/******************************************************************************/ + +/* Function: xexthandler + * + * Purpose: To parse the directive: exthandler + * + * the path of the plugin to be loaded + * a string parameter (e.g. a config file) that is + * passed to the initialization of the plugin + * + * Output: 0 upon success or !0 upon failure. + */ + +int XrdHttpProtocol::xexthandler(XrdOucStream & Config) { + char *val, valbuf[1024]; + char *parm; + + // Get the path + // + val = Config.GetWord(); + if (!val || !val[0]) { + eDest.Emsg("Config", "No http external handler plugin specified."); + return 1; + } else { + strcpy(valbuf, val); + parm = Config.GetWord(); + + // Try to load the plugin (if available) that extracts info from the user cert/proxy + // + if (LoadExtHandler(&eDest, valbuf, parm)) + return 1; + } + + + return 0; +} + + + + + + + + + + /******************************************************************************/ /* x s s l c a d i r */ /******************************************************************************/ @@ -2220,6 +2344,11 @@ int XrdHttpProtocol::doStat(char *fname) { // Loads the SecXtractor plugin, if available int XrdHttpProtocol::LoadSecXtractor(XrdSysError *myeDest, const char *libName, const char *libParms) { + + + // We don't want to load it more than once + if (secxtractor) return 1; + XrdVersionInfo *myVer = &XrdVERSIONINFOVAR(XrdgetProtocol); XrdOucPinLoader myLib(myeDest, myVer, "secxtractorlib", libName); XrdHttpSecXtractor *(*ep)(XrdHttpSecXtractorArgs); @@ -2231,3 +2360,26 @@ int XrdHttpProtocol::LoadSecXtractor(XrdSysError *myeDest, const char *libName, myLib.Unload(); return 1; } + + +// Loads the external handler plugin, if available +int XrdHttpProtocol::LoadExtHandler(XrdSysError *myeDest, const char *libName, + const char *libParms) { + + // We don't want to load it more than once + if (exthandler) return 1; + + XrdVersionInfo *myVer = &XrdVERSIONINFOVAR(XrdgetProtocol); + XrdOucPinLoader myLib(myeDest, myVer, "exthandlerlib", libName); + XrdHttpExtHandler *(*ep)(XrdHttpExtHandlerArgs); + + // Get the entry point of the object creator + // + ep = (XrdHttpExtHandler *(*)(XrdHttpExtHandlerArgs))(myLib.Resolve("XrdHttpGetExtHandler")); + if (ep && (exthandler = ep(myeDest, NULL, libParms))) return 0; + myLib.Unload(); + return 1; +} + + + diff --git a/src/XrdHttp/XrdHttpProtocol.hh b/src/XrdHttp/XrdHttpProtocol.hh index bc6a122e86d..92442880e31 100644 --- a/src/XrdHttp/XrdHttpProtocol.hh +++ b/src/XrdHttp/XrdHttpProtocol.hh @@ -67,6 +67,7 @@ class XrdBuffer; class XrdLink; class XrdXrootdProtocol; class XrdHttpSecXtractor; +class XrdHttpExtHandler; struct XrdVersionInfo; class XrdOucGMap; @@ -74,6 +75,7 @@ class XrdOucGMap; class XrdHttpProtocol : public XrdProtocol { friend class XrdHttpReq; + friend class XrdHttpExtReq; public: @@ -116,12 +118,9 @@ public: static XrdObjectQ ProtStack; XrdObject ProtLink; - - - /// Sends a basic response. If the length is < 0 then it is calculated internally - int SendSimpleResp(int code, char *desc, char *header_to_add, char *body, long long bodylen); - - + + /// Authentication area + XrdSecEntity SecEntity; private: @@ -155,6 +154,7 @@ private: static int xsslcert(XrdOucStream &Config); static int xsslkey(XrdOucStream &Config); static int xsecxtractor(XrdOucStream &Config); + static int xexthandler(XrdOucStream & Config); static int xsslcadir(XrdOucStream &Config); static int xdesthttps(XrdOucStream &Config); static int xlistdeny(XrdOucStream &Config); @@ -169,9 +169,15 @@ private: static int xsecretkey(XrdOucStream &Config); static XrdHttpSecXtractor *secxtractor; + static XrdHttpExtHandler *exthandler; + // Loads the SecXtractor plugin, if available static int LoadSecXtractor(XrdSysError *eDest, const char *libName, const char *libParms); + + // Loads the ExtHandler plugin, if available + static int LoadExtHandler(XrdSysError *eDest, const char *libName, + const char *libParms); /// Circular Buffer used to read the request XrdBuffer *myBuff; @@ -194,7 +200,12 @@ private: int BuffgetData(int blen, char **data, bool wait); /// Copy a full line of text from the buffer into dest. Zero if no line can be found in the buffer int BuffgetLine(XrdOucString &dest); - + + + + /// Sends a basic response. If the length is < 0 then it is calculated internally + int SendSimpleResp(int code, char *desc, char *header_to_add, char *body, long long bodylen); + /// Gets a string that represents the IP address of the client. Must be freed char *GetClientIPStr(); @@ -224,6 +235,8 @@ private: /// connection being established bool ssldone; + + static XrdCryptoFactory *myCryptoFactory; protected: @@ -253,10 +266,10 @@ protected: /// The link we are bound to XrdLink *Link; - - /// Authentication area - XrdSecEntity SecEntity; - + + /// Our IP address, as a string. Please remember that this may not be unique for + /// a given machine, hence we need to keep it here and recompute ad every new connection. + char *Addr_str; /// The instance of the DN mapper. Created only when a valid path is given static XrdOucGMap *servGMap; // Grid mapping service @@ -285,9 +298,6 @@ protected: /// Our port, as a string static char * Port_str; - - /// Our IP address, as a string - static char * Addr_str; /// Windowsize static int Window; diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc index d6256cac77f..8102f563ba0 100644 --- a/src/XrdHttp/XrdHttpReq.cc +++ b/src/XrdHttp/XrdHttpReq.cc @@ -39,6 +39,7 @@ #include "XrdVersion.hh" #include "XrdHttpReq.hh" #include "XrdHttpTrace.hh" +#include "XrdHttpExtHandler.hh" #include #include #include @@ -166,7 +167,7 @@ int XrdHttpReq::parseLine(char *line, int len) { // by looking at the first token of the line // The token is key // The value is val - + // Screen out the needed header lines if (!strcmp(key, "Connection")) { @@ -193,6 +194,11 @@ int XrdHttpReq::parseLine(char *line, int len) { sendcontinue = true; } + // We memorize the heaers also as a string + // because external plugins may need to process it differently + std::string ss = val; + trim(ss); + allheaders[key] = ss; line[pos] = ':'; } @@ -340,6 +346,8 @@ int XrdHttpReq::parseFirstLine(char *line, int len) { request = rtHEAD; } else if (!strcmp(key, "PUT")) { request = rtPUT; + } else if (!strcmp(key, "POST")) { + request = rtPOST; } else if (!strcmp(key, "PATCH")) { request = rtPATCH; } else if (!strcmp(key, "OPTIONS")) { @@ -357,7 +365,8 @@ int XrdHttpReq::parseFirstLine(char *line, int len) { } else { request = rtUnknown; } - + + requestverb = key; line[pos] = ' '; } @@ -506,7 +515,7 @@ bool XrdHttpReq::Done(XrdXrootd::Bridge::Context & info) { TRACE(REQ, " XrdHttpReq::Done"); xrdresp = kXR_ok; - this->iovN = 0; + //this->iovN = 0; if (PostProcessHTTPReq(true)) reset(); @@ -527,6 +536,10 @@ bool XrdHttpReq::Error(XrdXrootd::Bridge::Context &info, //!< the result context if (PostProcessHTTPReq()) reset(); + // Second part of the ugly hack on stat() + if ((request == rtGET) && (xrdreq.header.requestid == ntohs(kXR_stat))) + return true; + return false; }; @@ -540,13 +553,31 @@ bool XrdHttpReq::Redir(XrdXrootd::Bridge::Context &info, //!< the result context char buf[512]; char hash[512]; hash[0] = '\0'; - + if (prot->isdesthttps) redirdest = "Location: https://"; else redirdest = "Location: http://"; - - redirdest += hname; + + // Beware, certain Ofs implementations (e.g. EOS) add opaque data directly to the host name + // This must be correctly treated here and appended to the opaque info + // that we may already have + char *pp = strchr((char *)hname, '?'); + char *vardata = 0; + if (pp) { + *pp = '\0'; + redirdest += hname; + vardata = pp+1; + int varlen = strlen(vardata); + + //Now extract the remaining, vardata points to it + while(*vardata == '&' && varlen) {vardata++; varlen--;} + + // Put the question mark back where it was + *pp = '?'; + } + else + redirdest += hname; if (port) { sprintf(buf, ":%d", port); @@ -554,8 +585,18 @@ bool XrdHttpReq::Redir(XrdXrootd::Bridge::Context &info, //!< the result context } redirdest += resource.c_str(); + + // Here we put back the opaque info, if any + if (vardata) { + redirdest += "?&"; + redirdest += vardata; + } + + // Shall we put also the opaque data of the request? Maybe not + //int l; + //if (opaque && opaque->Env(l)) + // redirdest += opaque->Env(l); - TRACE(REQ, " XrdHttpReq::Redir Redirecting to " << redirdest); time_t timenow = 0; if (!prot->isdesthttps && prot->ishttps) { @@ -573,7 +614,8 @@ bool XrdHttpReq::Redir(XrdXrootd::Bridge::Context &info, //!< the result context } else appendOpaque(redirdest, 0, 0, 0); - + + TRACE(REQ, " XrdHttpReq::Redir Redirecting to " << redirdest); prot->SendSimpleResp(302, NULL, (char *) redirdest.c_str(), 0, 0); @@ -605,17 +647,18 @@ void XrdHttpReq::appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, s += hash; s += "&xrdhttptime="; - char buf[32]; + char buf[256]; sprintf(buf, "%ld", tnow); s += buf; if (secent) { if (secent->name) { s += "&xrdhttpname="; - char *s1 = quote(secent->name); - s += s1; - free(s1); + if (s1) { + s += s1; + free(s1); + } } if (secent->vorg) { @@ -623,10 +666,23 @@ void XrdHttpReq::appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, s += secent->vorg; } -// if (secent->host) { -// s += "&xrdhttphost="; -// s += secent->host; -// } + if (secent->host) { + s += "&xrdhttphost="; + char *s1 = quote(secent->host); + if (s1) { + s += s1; + free(s1); + } + } + + if (secent->moninfo) { + s += "&xrdhttpdn="; + char *s1 = quote(secent->moninfo); + if (s1) { + s += s1; + free(s1); + } + } } } @@ -642,6 +698,10 @@ void XrdHttpReq::parseResource(char *res) { // Not found, then it's just a filename if (!p) { resource.assign(res, 0); + char *buf = unquote((char *)resource.c_str()); + resource.assign(buf, 0); + resourceplusopaque.assign(buf, 0); + free(buf); // Sanitize the resource string, removing double slashes int pos = 0; @@ -659,10 +719,10 @@ void XrdHttpReq::parseResource(char *res) { int cnt = p - res; // Number of chars to copy resource.assign(res, 0, cnt - 1); - // Whatever comes after is opaque data to be parsed - if (strlen(p) > 1) - opaque = new XrdOucEnv(p + 1); - + char *buf = unquote((char *)resource.c_str()); + resource.assign(buf, 0); + free(buf); + // Sanitize the resource string, removing double slashes int pos = 0; do { @@ -671,14 +731,42 @@ void XrdHttpReq::parseResource(char *res) { resource.erase(pos, 1); } while (pos != STR_NPOS); + resourceplusopaque = resource; + // Whatever comes after is opaque data to be parsed + if (strlen(p) > 1) { + buf = unquote(p + 1); + opaque = new XrdOucEnv(buf); + resourceplusopaque.append('?'); + resourceplusopaque.append(buf); + free(buf); + } + + + } int XrdHttpReq::ProcessHTTPReq() { kXR_int32 l; + + + // Verify if we have an external handler for this request + if (prot->exthandler && prot->exthandler->MatchesPath(this->resource.c_str())) { + XrdHttpExtReq xreq(this, prot); + int r = prot->exthandler->ProcessReq(xreq); + reset(); + if (!r) return 1; // All went fine, response sent + if (r < 0) return -1; // There was a hard error... close the connection + + return 1; // There was an error and a response was sent + + } + + + // - // Prepare the data part + // Here we process the request locally // switch (request) { @@ -693,7 +781,7 @@ int XrdHttpReq::ProcessHTTPReq() { { // Do a Stat - if (prot->doStat((char *) resource.c_str())) { + if (prot->doStat((char *) resourceplusopaque.c_str())) { prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0); return -1; } @@ -760,13 +848,12 @@ int XrdHttpReq::ProcessHTTPReq() { } - + switch (reqstate) { case 0: // Stat() - // Do a Stat - if (prot->doStat((char *) resource.c_str())) { + if (prot->doStat((char *) resourceplusopaque.c_str())) { XrdOucString errmsg = "Error stating"; errmsg += resource.c_str(); prot->SendSimpleResp(404, NULL, NULL, (char *) errmsg.c_str(), 0); @@ -800,7 +887,7 @@ int XrdHttpReq::ProcessHTTPReq() { string res; - res = resource.c_str(); + res = resourceplusopaque.c_str(); //res += "?xrd.dirstat=1"; // --------- DIRLIST @@ -824,12 +911,12 @@ int XrdHttpReq::ProcessHTTPReq() { // --------- OPEN memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.open.requestid = htons(kXR_open); - l = resource.length() + 1; + l = resourceplusopaque.length() + 1; xrdreq.open.dlen = htonl(l); xrdreq.open.mode = 0; xrdreq.open.options = htons(kXR_retstat | kXR_open_read); - if (!prot->Bridge->Run((char *) &xrdreq, (char *) resource.c_str(), l)) { + if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) { prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0); return -1; } @@ -846,66 +933,79 @@ int XrdHttpReq::ProcessHTTPReq() { default: // Read() or Close() { - if ( ((reqstate == 3) && (rwOps.size() > 1)) || - (writtenbytes >= filesize) ) { - // Close() if this was a readv or we have finished, otherwise read the next chunk - - // --------- CLOSE - memset(&xrdreq, 0, sizeof (ClientRequest)); - xrdreq.close.requestid = htons(kXR_close); - memcpy(xrdreq.close.fhandle, fhandle, 4); + if ( ((reqstate == 3) && (rwOps.size() > 1)) || + (writtenbytes >= filesize) ) { - if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) { - prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run close request.", 0); - return -1; - } + // Close() if this was a readv or we have finished, otherwise read the next chunk - // We have finished - return 1; + // --------- CLOSE - } + memset(&xrdreq, 0, sizeof (ClientRequest)); + xrdreq.close.requestid = htons(kXR_close); + memcpy(xrdreq.close.fhandle, fhandle, 4); + + if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) { + prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run close request.", 0); + return -1; + } + + // We have finished + return 1; + + } if (rwOps.size() <= 1) { // No chunks or one chunk... Request the whole file or single read - // - long l; + + long l; + long long offs; + // --------- READ memset(&xrdreq, 0, sizeof (xrdreq)); xrdreq.read.requestid = htons(kXR_read); memcpy(xrdreq.read.fhandle, fhandle, 4); xrdreq.read.dlen = 0; - + if (rwOps.size() == 0) { - l = (long)min(filesize-writtenbytes, (long long)1024*1024); + l = (long)min(filesize-writtenbytes, (long long)1024*1024); + offs = writtenbytes; xrdreq.read.offset = htonll(writtenbytes); xrdreq.read.rlen = htonl(l); } else { - l = min(rwOps[0].byteend - rwOps[0].bytestart + 1 - writtenbytes, (long long)1024*1024); - xrdreq.read.offset = htonll(rwOps[0].bytestart + writtenbytes); + l = min(rwOps[0].byteend - rwOps[0].bytestart + 1 - writtenbytes, (long long)1024*1024); + offs = rwOps[0].bytestart + writtenbytes; + xrdreq.read.offset = htonll(offs); xrdreq.read.rlen = htonl(l); } - if (prot->ishttps) { + if (prot->ishttps) { if (!prot->Bridge->setSF((kXR_char *) fhandle, false)) { TRACE(REQ, " XrdBridge::SetSF(false) failed."); } } + + if (l <= 0) { - if (l < 0) { - TRACE(ALL, " Data sizes mismatch."); - return -1; - } - else { - TRACE(ALL, " No more bytes to send."); - reset(); - return 1; - } - } - - - + if (l < 0) { + TRACE(ALL, " Data sizes mismatch."); + return -1; + } + else { + TRACE(ALL, " No more bytes to send."); + reset(); + return 1; + } + } + + if ((offs >= filesize) || (offs+l > filesize)) { + TRACE(ALL, " Requested range " << l << "@" << offs << + " is past the end of file (" << filesize << ")"); + //prot->SendSimpleResp(522, NULL, NULL, (char *) "Invalid range request", 0); + return -1; + } + if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) { prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run read request.", 0); return -1; @@ -943,12 +1043,12 @@ int XrdHttpReq::ProcessHTTPReq() { // --------- OPEN for write! memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.open.requestid = htons(kXR_open); - l = resource.length() + 1; + l = resourceplusopaque.length() + 1; xrdreq.open.dlen = htonl(l); xrdreq.open.mode = htons(kXR_ur | kXR_uw | kXR_gw | kXR_gr | kXR_or); xrdreq.open.options = htons(kXR_mkpath | kXR_open_updt | kXR_new); - if (!prot->Bridge->Run((char *) &xrdreq, (char *) resource.c_str(), l)) { + if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) { prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0); return -1; } @@ -1034,13 +1134,13 @@ int XrdHttpReq::ProcessHTTPReq() { // --------- STAT is always the first step memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.stat.requestid = htons(kXR_stat); - string s = resource.c_str(); + string s = resourceplusopaque.c_str(); - l = resource.length() + 1; + l = resourceplusopaque.length() + 1; xrdreq.stat.dlen = htonl(l); - if (!prot->Bridge->Run((char *) &xrdreq, (char *) resource.c_str(), l)) { + if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) { prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0); return -1; } @@ -1055,7 +1155,7 @@ int XrdHttpReq::ProcessHTTPReq() { memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.rmdir.requestid = htons(kXR_rmdir); - string s = resource.c_str(); + string s = resourceplusopaque.c_str(); l = s.length() + 1; xrdreq.rmdir.dlen = htonl(l); @@ -1069,7 +1169,7 @@ int XrdHttpReq::ProcessHTTPReq() { memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.rm.requestid = htons(kXR_rm); - string s = resource.c_str(); + string s = resourceplusopaque.c_str(); l = s.length() + 1; xrdreq.rm.dlen = htonl(l); @@ -1128,13 +1228,13 @@ int XrdHttpReq::ProcessHTTPReq() { // --------- STAT is always the first step memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.stat.requestid = htons(kXR_stat); - string s = resource.c_str(); + string s = resourceplusopaque.c_str(); - l = resource.length() + 1; + l = resourceplusopaque.length() + 1; xrdreq.stat.dlen = htonl(l); - if (!prot->Bridge->Run((char *) &xrdreq, (char *) resource.c_str(), l)) { + if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) { prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0); return -1; } @@ -1159,7 +1259,7 @@ int XrdHttpReq::ProcessHTTPReq() { memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.dirlist.requestid = htons(kXR_dirlist); - string s = resource.c_str(); + string s = resourceplusopaque.c_str(); xrdreq.dirlist.options[0] = kXR_dstat; //s += "?xrd.dirstat=1"; @@ -1186,7 +1286,7 @@ int XrdHttpReq::ProcessHTTPReq() { memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.mkdir.requestid = htons(kXR_mkdir); - string s = resource.c_str(); + string s = resourceplusopaque.c_str(); xrdreq.mkdir.options[0] = (kXR_char) kXR_mkpath; l = s.length() + 1; @@ -1207,7 +1307,7 @@ int XrdHttpReq::ProcessHTTPReq() { memset(&xrdreq, 0, sizeof (ClientRequest)); xrdreq.mv.requestid = htons(kXR_mv); - string s = resource.c_str(); + string s = resourceplusopaque.c_str(); s += " "; char buf[256]; @@ -1271,9 +1371,13 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) { switch (request) { case XrdHttpReq::rtUnknown: + { + prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 1", 0); + return -1; + } case XrdHttpReq::rtMalformed: { - prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed", 0); + prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 2", 0); return -1; } case XrdHttpReq::rtHEAD: @@ -1455,15 +1559,15 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) { stringresp += prot->SecEntity.vorg; } - if (prot->SecEntity.name) { + if (prot->SecEntity.moninfo) { stringresp += " DN: "; - stringresp += prot->SecEntity.name; - } - else - if (prot->SecEntity.moninfo) { + stringresp += prot->SecEntity.moninfo; + } else + if (prot->SecEntity.name) { stringresp += " DN: "; - stringresp += prot->SecEntity.moninfo; - } + stringresp += prot->SecEntity.name; + } + if (prot->SecEntity.role) { stringresp += " Role: "; @@ -1501,53 +1605,72 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) { } else { - // If it's a dir then treat it as a dir by redirecting to ourself with one more slash - if (xrderrcode == 3016) { - - string res = "Location: http://"; - if (prot->ishttps) res = "Location: https://"; - res += host; - res += resource.c_str(); - res += "/"; - prot->SendSimpleResp(302, NULL, (char *) res.c_str(), NULL, 0); - return 1; - } - switch (reqstate) { case 0: //stat { - if (xrdresp != kXR_ok) { + // Ugly hack. Be careful with EOS! Test with vanilla XrdHTTP and EOS, separately + // A 404 on the preliminary stat() is fatal only + // in a manager. A non-manager will ignore the result and try anyway to open the file + // + if (xrdresp == kXR_ok) { + + if (iovN > 0) { + + // Now parse the stat info + TRACEI(REQ, "Stat for GET " << resource << " stat=" << (char *) iovP[0].iov_base); + + long dummyl; + sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld", + &dummyl, + &filesize, + &fileflags, + &filemodtime); + } + else + TRACEI(REQ, "Can't find the stat information for '" << resource << "' Internal error?"); + + return 0; + + } + + // We are here if the request failed + + if (prot->myRole == kXR_isManager) { prot->SendSimpleResp(404, NULL, NULL, (char *) "File not found.", 0); return -1; } - if (iovN > 0) { - - // Now parse the stat info - TRACEI(REQ, "Stat for GET " << resource << " stat=" << (char *) iovP[0].iov_base); - - long dummyl; - sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld", - &dummyl, - &filesize, - &fileflags, - &filemodtime); - } + // We are here in the case of a negative response in a non-manager return 0; } case 1: //open { - + if (xrdresp == kXR_ok) { getfhandle(); - - + + // Now parse the stat info if we still don't have it + if (filesize == 0) { + if (iovP[1].iov_len > 1) { + TRACEI(REQ, "Stat for GET " << resource << " stat=" << (char *) iovP[1].iov_base); + + long dummyl; + sscanf((const char *) iovP[1].iov_base, "%ld %lld %ld %ld", + &dummyl, + &filesize, + &fileflags, + &filemodtime); + } + else + TRACEI(ALL, "GET returned no STAT information. Internal error?"); + } + if (rwOps.size() == 0) { // Full file. @@ -1592,7 +1715,14 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) { } else { - + + // If it's a dir then we are in the wrong place and we did the wrong thing. + //if (xrderrcode == 3016) { + // fileflags &= kXR_isDir; + // reqstate--; + // return 0; + //} + prot->SendSimpleResp(404, NULL, NULL, (char *) "Error man!", 0); return -1; } @@ -1603,6 +1733,7 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) { } default: //read or readv { + // Close() if this was the third state of a readv, otherwise read the next chunk if ((reqstate == 3) && (ntohs(xrdreq.header.requestid) == kXR_readv)) return 1; @@ -2073,7 +2204,8 @@ void XrdHttpReq::reset() { ralist = 0; request = rtUnknown; - resource[0] = 0; + resource = ""; + allheaders.clear(); headerok = false; keepalive = true; diff --git a/src/XrdHttp/XrdHttpReq.hh b/src/XrdHttp/XrdHttpReq.hh index 783086282f0..e69f89b8b85 100644 --- a/src/XrdHttp/XrdHttpReq.hh +++ b/src/XrdHttp/XrdHttpReq.hh @@ -46,6 +46,7 @@ #include #include +#include //#include //#include @@ -152,16 +153,25 @@ public: rtDELETE, rtPROPFIND, rtMKCOL, - rtMOVE + rtMOVE, + rtPOST }; /// The request we got ReqType request; - - /// The resource specified by the request, complete with all opaque data + std::string requestverb; + + // We have to keep the headers for possible further processing + // by external plugins + std::map allheaders; + + /// The resource specified by the request, stripped of opaque data XrdOucString resource; /// The opaque data, after parsing XrdOucEnv *opaque; + /// The resource specified by the request, including all the opaque data + XrdOucString resourceplusopaque; + /// Tells if we have finished reading the header bool headerok; @@ -341,7 +351,7 @@ public: virtual int File(XrdXrootd::Bridge::Context &info, //!< the result context int dlen //!< byte count - ); + ); //----------------------------------------------------------------------------- //! Redirect the client to another host:port. @@ -369,5 +379,9 @@ public: }; + + +void trim(std::string &str); + #endif /* XRDHTTPREQ_HH */ diff --git a/src/XrdHttp/XrdHttpUtils.cc b/src/XrdHttp/XrdHttpUtils.cc index 3c1b233471f..9fe0db0fb4b 100644 --- a/src/XrdHttp/XrdHttpUtils.cc +++ b/src/XrdHttp/XrdHttpUtils.cc @@ -54,7 +54,6 @@ #include "XrdSec/XrdSecEntity.hh" # include "sys/param.h" #include "XrdOuc/XrdOucString.hh" -static pthread_key_t cm_key; #if OPENSSL_VERSION_NUMBER < 0x10100000L static HMAC_CTX* HMAC_CTX_new() { @@ -210,9 +209,6 @@ void calcHashes( char buf[64]; struct tm tms; - // set so key destructor can trigger removal of - // libcrypto error state when the thread finishes - pthread_setspecific(cm_key, &cm_key); if (!hash) { return; @@ -257,6 +253,10 @@ void calcHashes( if (secent->host) HMAC_Update(ctx, (const unsigned char *) secent->host, strlen(secent->host) + 1); + + if (secent->moninfo) + HMAC_Update(ctx, (const unsigned char *) secent->moninfo, + strlen(secent->moninfo) + 1); localtime_r(&tim, &tms); strftime(buf, sizeof (buf), "%s", &tms); @@ -321,7 +321,7 @@ char *unquote(char *str) { char *quote(char *str) { int l = strlen(str); - char *r = (char *) malloc(l + 1); + char *r = (char *) malloc(l*3 + 1); r[0] = '\0'; int i, j = 0; @@ -333,6 +333,22 @@ char *quote(char *str) { strcpy(r + j, "%20"); j += 3; break; + case '[': + strcpy(r + j, "%5B"); + j += 3; + break; + case ']': + strcpy(r + j, "%5D"); + j += 3; + break; + case ':': + strcpy(r + j, "%3A"); + j += 3; + break; + case '/': + strcpy(r + j, "%2F"); + j += 3; + break; default: r[j++] = c; } From 4ed3f11de0f64cd4e12e82b4ea65eb23cfc87980 Mon Sep 17 00:00:00 2001 From: Fabrizio Furano Date: Wed, 19 Jul 2017 15:24:40 +0200 Subject: [PATCH 2/2] Move the ExtHandler to the private headers --- src/XrdHeaders.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/XrdHeaders.cmake b/src/XrdHeaders.cmake index 1275195557e..27040c6224a 100644 --- a/src/XrdHeaders.cmake +++ b/src/XrdHeaders.cmake @@ -104,7 +104,7 @@ set( XROOTD_PUBLIC_HEADERS XrdXrootd/XrdXrootdMonData.hh XrdXrootd/XrdXrootdBridge.hh XrdHttp/XrdHttpSecXtractor.hh - XrdHttp/XrdHttpExtHandler.hh + ) set( XROOTD_PRIVATE_HEADERS @@ -133,7 +133,7 @@ set( XROOTD_PRIVATE_HEADERS XrdOss/XrdOssError.hh XrdOuc/XrdOucExport.hh XrdOuc/XrdOucPList.hh - + XrdHttp/XrdHttpExtHandler.hh ) install_headers(