From: Alex Rousskov Date: Thu, 17 Feb 2011 19:27:54 +0000 (-0700) Subject: libecap v0.2.0 options support: supply client IP and user name to eCAP. X-Git-Tag: take03~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=22fff3bf355cb43a0162edbeb768ff167cd53e71;p=thirdparty%2Fsquid.git libecap v0.2.0 options support: supply client IP and user name to eCAP. Squid now uses libecap::Options API to send client IP and user name meta-information to the eCAP adapter transaction, just like ICAP code does when talking to an ICAP service transaction. Renamed related icap_* options to their more general adaptation_* equivalents because they now control both eCAP and ICAP behavior. Old icap_* names are deprecated but still available. Converted eCAP service configuration code to support the new Options API, polished. --- diff --git a/src/adaptation/Config.cc b/src/adaptation/Config.cc index 612a1f3367..f789686ff7 100644 --- a/src/adaptation/Config.cc +++ b/src/adaptation/Config.cc @@ -47,6 +47,9 @@ bool Adaptation::Config::Enabled = false; char *Adaptation::Config::masterx_shared_name = NULL; int Adaptation::Config::service_iteration_limit = 16; +int Adaptation::Config::send_client_ip = false; +int Adaptation::Config::send_username = false; +int Adaptation::Config::use_indirect_client = true; Adaptation::ServiceConfig* diff --git a/src/adaptation/Config.h b/src/adaptation/Config.h index 6ae34a4817..f9f8dfd8d1 100644 --- a/src/adaptation/Config.h +++ b/src/adaptation/Config.h @@ -32,15 +32,16 @@ public: // these are global squid.conf options, documented elsewhere static char *masterx_shared_name; // global TODO: do we need TheConfig? static int service_iteration_limit; + static int send_client_ip; + static int send_username; + static int use_indirect_client; + // Options below are accessed via Icap::TheConfig or Ecap::TheConfig // TODO: move ICAP-specific options to Icap::Config and add TheConfig int onoff; - int send_client_ip; - int send_client_username; int service_failure_limit; time_t oldest_service_failure; int service_revival_delay; - int icap_uses_indirect_client; typedef Vector ServiceConfigs; ServiceConfigs serviceConfigs; diff --git a/src/adaptation/ecap/Host.cc b/src/adaptation/ecap/Host.cc index 7903cb887b..370645d74e 100644 --- a/src/adaptation/ecap/Host.cc +++ b/src/adaptation/ecap/Host.cc @@ -18,6 +18,7 @@ const libecap::Name Adaptation::Ecap::protocolIcp("ICP", libecap::Name::NextId() #if USE_HTCP const libecap::Name Adaptation::Ecap::protocolHtcp("Htcp", libecap::Name::NextId()); #endif +const libecap::Name Adaptation::Ecap::metaBypassable("bypassable", libecap::Name::NextId()); /// the host application (i.e., Squid) wrapper registered with libecap static libecap::shared_ptr TheHost; @@ -47,6 +48,9 @@ Adaptation::Ecap::Host::Host() #if USE_HTCP protocolHtcp.assignHostId(PROTO_HTCP); #endif + + // allows adapter to safely ignore this in adapter::Service::configure() + metaBypassable.assignHostId(1); } std::string diff --git a/src/adaptation/ecap/Host.h b/src/adaptation/ecap/Host.h index 6ec696b907..846a707aae 100644 --- a/src/adaptation/ecap/Host.h +++ b/src/adaptation/ecap/Host.h @@ -47,6 +47,7 @@ extern const libecap::Name protocolIcp; #if USE_HTCP extern const libecap::Name protocolHtcp; #endif +extern const libecap::Name metaBypassable; ///< an ecap_service parameter } // namespace Ecap } // namespace Adaptation diff --git a/src/adaptation/ecap/ServiceRep.cc b/src/adaptation/ecap/ServiceRep.cc index e95296c81d..c22d060bb4 100644 --- a/src/adaptation/ecap/ServiceRep.cc +++ b/src/adaptation/ecap/ServiceRep.cc @@ -4,10 +4,11 @@ #include "squid.h" #include #include -#include +#include #include #include #include "adaptation/ecap/Config.h" +#include "adaptation/ecap/Host.h" #include "adaptation/ecap/ServiceRep.h" #include "adaptation/ecap/XactionRep.h" #include "base/TextException.h" @@ -15,30 +16,59 @@ // configured eCAP service wrappers static std::list TheServices; +namespace Adaptation +{ +namespace Ecap +{ + /// wraps Adaptation::Ecap::ServiceConfig to allow eCAP visitors -class ConfigRep: public libecap::Config +class ConfigRep: public libecap::Options { public: typedef Adaptation::Ecap::ServiceConfig Master; typedef libecap::Name Name; typedef libecap::Area Area; - ConfigRep(const Master &aMaster): master(aMaster) {} + ConfigRep(const Master &aMaster); - // libecap::Config API - virtual void visitEach(libecap::NamedValueVisitor &visitor) const; + // libecap::Options API + virtual const libecap::Area option(const libecap::Name &name) const; + virtual void visitEachOption(libecap::NamedValueVisitor &visitor) const; const Master &master; ///< the configuration being wrapped }; +} // namespace Ecap +} // namespace Adaptation + + +Adaptation::Ecap::ConfigRep::ConfigRep(const Master &aMaster): master(aMaster) +{ +} + +const libecap::Area +Adaptation::Ecap::ConfigRep::option(const libecap::Name &name) const +{ + // we may supply the params we know about, but only when names have host ID + if (name == metaBypassable) + return Area(master.bypass ? "1" : "0", 1); + + // TODO: We could build a by-name index, but is it worth it? Good adapters + // should use visitEachOption() instead, to check for name typos/errors. + typedef Master::Extensions::const_iterator MECI; + for (MECI i = master.extensions.begin(); i != master.extensions.end(); ++i) { + if (name == i->first) + return Area(i->second.data(), i->second.size()); + } + + return Area(); +} + void -ConfigRep::visitEach(libecap::NamedValueVisitor &visitor) const +Adaptation::Ecap::ConfigRep::visitEachOption(libecap::NamedValueVisitor &visitor) const { // we may supply the params we know about too, but only if we set host ID - static const Name optBypass("bypassable"); - if (!optBypass.assignedHostId()) - optBypass.assignHostId(1); // allows adapter to safely ignore this - visitor.visit(optBypass, Area(master.bypass ? "1" : "0", 1)); + visitor.visit(metaBypassable, Area(master.bypass ? "1" : "0", 1)); // visit adapter-specific options (i.e., those not recognized by Squid) typedef Master::Extensions::const_iterator MECI; @@ -70,7 +100,7 @@ Adaptation::Ecap::ServiceRep::finalize() theService = FindAdapterService(cfg().uri); if (theService) { debugs(93,3, HERE << "configuring eCAP service: " << theService->uri()); - ConfigRep cfgRep(dynamic_cast(cfg())); + const ConfigRep cfgRep(dynamic_cast(cfg())); theService->configure(cfgRep); debugs(93,3, HERE << "starting eCAP service: " << theService->uri()); diff --git a/src/adaptation/ecap/XactionRep.cc b/src/adaptation/ecap/XactionRep.cc index 7a0876a49e..17e6861369 100644 --- a/src/adaptation/ecap/XactionRep.cc +++ b/src/adaptation/ecap/XactionRep.cc @@ -4,11 +4,14 @@ #include "squid.h" #include #include +#include +#include #include #include "HttpRequest.h" #include "HttpReply.h" #include "SquidTime.h" #include "adaptation/ecap/XactionRep.h" +#include "adaptation/ecap/Config.h" #include "adaptation/Initiator.h" #include "base/TextException.h" @@ -53,6 +56,67 @@ Adaptation::Ecap::XactionRep::service() return *theService; } +const libecap::Area +Adaptation::Ecap::XactionRep::option(const libecap::Name &name) const +{ + if (name == libecap::metaClientIp) + return clientIpValue(); + if (name == libecap::metaUserName) + return usernameValue(); + // TODO: metaServerIp, metaAuthenticatedUser, metaAuthenticatedGroups, and + // Adaptation::Config::masterx_shared_name + return libecap::Area(); +} + +void +Adaptation::Ecap::XactionRep::visitEachOption(libecap::NamedValueVisitor &visitor) const +{ + if (const libecap::Area value = clientIpValue()) + visitor.visit(libecap::metaClientIp, value); + if (const libecap::Area value = usernameValue()) + visitor.visit(libecap::metaUserName, value); + // TODO: metaServerIp, metaAuthenticatedUser, metaAuthenticatedGroups, and + // Adaptation::Config::masterx_shared_name +} + +const libecap::Area +Adaptation::Ecap::XactionRep::clientIpValue() const +{ + const HttpRequest *request = dynamic_cast(theCauseRep ? + theCauseRep->raw().header : theVirginRep.raw().header); + Must(request); + // TODO: move this logic into HttpRequest::clientIp(bool) and + // HttpRequest::clientIpString(bool) and reuse everywhere + if (TheConfig.send_client_ip && request) { + Ip::Address client_addr; +#if FOLLOW_X_FORWARDED_FOR + if (TheConfig.use_indirect_client) { + client_addr = request->indirect_client_addr; + } else +#endif + client_addr = request->client_addr; + if (!client_addr.IsAnyAddr() && !client_addr.IsNoAddr()) { + char ntoabuf[MAX_IPSTRLEN] = ""; + client_addr.NtoA(ntoabuf,MAX_IPSTRLEN); + return libecap::Area::FromTempBuffer(ntoabuf, strlen(ntoabuf)); + } + } + return libecap::Area(); +} + +const libecap::Area +Adaptation::Ecap::XactionRep::usernameValue() const +{ + const HttpRequest *request = dynamic_cast(theCauseRep ? + theCauseRep->raw().header : theVirginRep.raw().header); + Must(request); + if (request->auth_user_request != NULL) { + if (char const *name = request->auth_user_request->username()) + return libecap::Area::FromTempBuffer(name, strlen(name)); + } + return libecap::Area(); +} + void Adaptation::Ecap::XactionRep::start() { diff --git a/src/adaptation/ecap/XactionRep.h b/src/adaptation/ecap/XactionRep.h index 1a073502c1..61163ee772 100644 --- a/src/adaptation/ecap/XactionRep.h +++ b/src/adaptation/ecap/XactionRep.h @@ -35,6 +35,8 @@ public: void master(const AdapterXaction &aMaster); // establish a link // libecap::host::Xaction API + virtual const libecap::Area option(const libecap::Name &name) const; + virtual void visitEachOption(libecap::NamedValueVisitor &visitor) const; virtual libecap::Message &virgin(); virtual const libecap::Message &cause(); virtual libecap::Message &adapted(); @@ -87,6 +89,9 @@ protected: void terminateMaster(); void scheduleStop(const char *reason); + const libecap::Area clientIpValue() const; + const libecap::Area usernameValue() const; + private: AdapterXaction theMaster; // the actual adaptation xaction we represent Adaptation::ServicePointer theService; ///< xaction's adaptation service diff --git a/src/adaptation/icap/ModXact.cc b/src/adaptation/icap/ModXact.cc index d093c09221..ae54334c4a 100644 --- a/src/adaptation/icap/ModXact.cc +++ b/src/adaptation/icap/ModXact.cc @@ -1357,7 +1357,7 @@ void Adaptation::Icap::ModXact::makeRequestHeaders(MemBuf &buf) if (TheConfig.send_client_ip && request) { Ip::Address client_addr; #if FOLLOW_X_FORWARDED_FOR - if (TheConfig.icap_uses_indirect_client) { + if (TheConfig.use_indirect_client) { client_addr = request->indirect_client_addr; } else #endif @@ -1366,7 +1366,7 @@ void Adaptation::Icap::ModXact::makeRequestHeaders(MemBuf &buf) buf.Printf("X-Client-IP: %s\r\n", client_addr.NtoA(ntoabuf,MAX_IPSTRLEN)); } - if (TheConfig.send_client_username && request) + if (TheConfig.send_username && request) makeUsernameHeader(request, buf); // fprintf(stderr, "%s\n", buf.content()); diff --git a/src/cf.data.pre b/src/cf.data.pre index e485a9adfd..321b8c1707 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -4103,12 +4103,12 @@ Example: broken_posts allow buggy_server DOC_END -NAME: icap_uses_indirect_client +NAME: adaptation_uses_indirect_client icap_uses_indirect_client COMMENT: on|off TYPE: onoff -IFDEF: FOLLOW_X_FORWARDED_FOR&&ICAP_CLIENT +IFDEF: FOLLOW_X_FORWARDED_FOR&&USE_ADAPTATION DEFAULT: on -LOC: Adaptation::Icap::TheConfig.icap_uses_indirect_client +LOC: Adaptation::Config::use_indirect_client DOC_START Controls whether the indirect client address (see follow_x_forwarded_for) instead of the @@ -6351,25 +6351,27 @@ DOC_START an ICAP server. DOC_END -NAME: icap_send_client_ip +NAME: adaptation_send_client_ip icap_send_client_ip TYPE: onoff -IFDEF: ICAP_CLIENT +IFDEF: USE_ADAPTATION COMMENT: on|off -LOC: Adaptation::Icap::TheConfig.send_client_ip +LOC: Adaptation::Config::send_client_ip DEFAULT: off DOC_START This adds the header "X-Client-IP" to ICAP requests. DOC_END -NAME: icap_send_client_username +NAME: adaptation_send_username icap_send_client_username TYPE: onoff -IFDEF: ICAP_CLIENT +IFDEF: USE_ADAPTATION COMMENT: on|off -LOC: Adaptation::Icap::TheConfig.send_client_username +LOC: Adaptation::Config::send_username DEFAULT: off DOC_START This sends authenticated HTTP client username (if available) to - the ICAP service. The username value is encoded based on the + the adaptation service. + + For ICAP, the username value is encoded based on the icap_client_username_encode option and is sent using the header specified by the icap_client_username_header option. DOC_END @@ -6380,7 +6382,7 @@ IFDEF: ICAP_CLIENT LOC: Adaptation::Icap::TheConfig.client_username_header DEFAULT: X-Client-Username DOC_START - ICAP request header name to use for send_client_username. + ICAP request header name to use for send_username. DOC_END NAME: icap_client_username_encode