diff -ruBEN polygraph-4.3.1/configure.in polygraph-4.3.1-mm/configure.in --- polygraph-4.3.1/configure.in 2011-03-03 22:02:32.000000000 +0000 +++ polygraph-4.3.1-mm/configure.in 2011-03-04 23:15:41.000000000 +0000 @@ -386,6 +386,149 @@ sleep 1; fi +AC_ARG_WITH(krb5-config, + AC_HELP_STRING([--with-krb5-config=PATH], + [specify path to krb5-config (default=detect)]), [ + case "$withval" in + yes) unset krb5confpath ;; + no) krb5confpath=no ;; + *) krb5confpath=$withval ;; + esac +]) +if test x"$krb5confpath" != "xno"; then + if test "x$krb5confpath" != "x"; then + if ! test -x "$krb5confpath"; then + AC_MSG_WARN([krb5-config '$krb5confpath' not executable, ignoring]) + AC_CHECK_PROG(ac_krb5_config, krb5-config, yes, no) + krb5confpath=krb5-config + fi + krb5_config_path=`dirname $krb5confpath` + AC_CHECK_PROG(ac_krb5_config, krb5-config, yes, no, $krb5_config_path) + else + AC_CHECK_PROG(ac_krb5_config,krb5-config,yes,no) + krb5confpath=krb5-config + fi +fi +if test "x$ac_krb5_config" = "xyes" ; then + ac_heimdal="`$krb5confpath --version 2>/dev/null | grep -i heimdal`" + if test "x$ac_heimdal" != "x" ; then + AC_DEFINE(HAVE_HEIMDAL_KERBEROS,1,[Define to 1 if you have Heimdal Kerberos]) + else + AC_DEFINE(HAVE_MIT_KERBEROS,1,[Define to 1 if you have MIT Kerberos]) + fi + KRB5INCS="`$krb5confpath --cflags krb5 2>/dev/null`" + KRB5LIBS="`$krb5confpath --libs krb5 2>/dev/null`" + KRB5INCS="`$krb5confpath --cflags gssapi 2>/dev/null` $KRB5INCS" + KRB5LIBS="`$krb5confpath --libs gssapi 2>/dev/null` $KRB5LIBS" + CPPFLAGS="$CPPFLAGS $KRB5INCS" + LIBS="$LIBS $KRB5LIBS" + AC_CHECK_HEADERS(gssapi.h gssapi/gssapi.h gssapi/gssapi_krb5.h) + if test "x$ac_heimdal" = "x" ; then + AC_CHECK_HEADERS(gssapi/gssapi_generic.h) + fi + + AC_CACHE_CHECK([for broken Heimdal krb5.h],squid_cv_broken_heimdal_krb5_h, [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +int +main(void) +{ + krb5_context context; + + krb5_init_context(&context); + + return 0; +} +]])], [ squid_cv_broken_heimdal_krb5_h=no ], [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#if defined(__cplusplus) +extern "C" { +#endif +#include +#if defined(__cplusplus) +} +#endif +int +main(void) +{ + krb5_context context; + + krb5_init_context(&context); + + return 0; +} +]])], [ squid_cv_broken_heimdal_krb5_h=yes ], [ squid_cv_broken_heimdal_krb5_h=no ]) + ]) + ]) + + if test "x$squid_cv_broken_heimdal_krb5_h" = "xyes"; then + AC_DEFINE(HAVE_BROKEN_HEIMDAL_KRB5_H, 1, [Define to 1 if Heimdal krb5.h is broken for C++]) + fi + AC_CHECK_HEADERS(krb5.h com_err.h et/com_err.h) + + dnl do compile check + AC_MSG_CHECKING([for working krb5]) + AC_TRY_RUN([ +#if HAVE_KRB5_H +#if HAVE_BROKEN_HEIMDAL_KRB5_H +extern "C" { +#include +} +#else +#include +#endif +#endif + +int +main(void) +{ + krb5_context context; + + krb5_init_context(&context); + + return 0; +} +], [unset no_krb5 + AC_DEFINE(HAVE_KRB5, 1, [KRB5 support]) + AC_MSG_RESULT(yes)], + AC_MSG_RESULT(no)) + + dnl do compile check + AC_MSG_CHECKING([for working gssapi]) + AC_TRY_RUN([ +#if HAVE_GSSAPI_H +#include +#elif HAVE_GSSAPI_GSSAPI_H +#include +#endif /* HAVE_GSSAPI_H */ + +#if !HAVE_HEIMDAL_KERBEROS +#if HAVE_GSSAPI_GSSAPI_EXT_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_EXT_H */ +#if HAVE_GSSAPI_GSSAPI_KRB5_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */ +#if HAVE_GSSAPI_GSSAPI_GENERIC_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_GENERIC_H */ +#endif /* HAVE_HEIMDAL_KERBEROS */ + +int +main(void) +{ + OM_uint32 val; + gss_OID_set set; + + gss_create_empty_oid_set(&val, &set); + + return 0; +} +], [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GSSAPI, 1, [GSSAPI support])], + [AC_MSG_RESULT(no)]) +fi + dnl check whether loganalizers/comparator should be built AC_MSG_CHECKING(whether to build comparator) AC_ARG_ENABLE(comparator, diff -ruBEN polygraph-4.3.1/src/client/CltCfg.cc polygraph-4.3.1-mm/src/client/CltCfg.cc --- polygraph-4.3.1/src/client/CltCfg.cc 2011-02-09 02:44:45.000000000 +0000 +++ polygraph-4.3.1-mm/src/client/CltCfg.cc 2011-03-04 23:15:41.000000000 +0000 @@ -51,7 +51,10 @@ theBusyPeriod(0), theIdlePeriodDur(0), theFtpProxyCycleCnt(0), theHttpProxyCycleCnt(0), theSocksProxyCycleCnt(0), thePipelineDepth(0), - theRecurRatio(-1), theSpnegoAuthRatio(-1), theEmbedRecurRatio(-1), + theRecurRatio(-1), theSpnegoAuthRatio(-1), preferKerberosAuth(false), + clearKerberosCache(false), theKerberosConfigPath(0), + theKerberosProxySPN(0), theKerberosServerSPN(0), + theEmbedRecurRatio(-1), theAbortProb(0), theAuthError(0), theWaitXactLmt(-1), theIcpPort(-1), theCredentialCycleCnt(0), theForeignWorld(0), @@ -84,7 +87,6 @@ delete thePopModel; delete theBusyPeriod; delete theContainerTags; - delete theAcceptedContentCodings; delete theForeignWorld; delete theWarmupPlan; delete thePostContentSel; @@ -105,6 +107,11 @@ theRobot->recurRatio(theRecurRatio); theRobot->spnegoRatio(theSpnegoAuthRatio); + theRobot->kerberos(preferKerberosAuth); + theRobot->kerberosClearCache(clearKerberosCache); + theKerberosConfigPath = theRobot->kerberosConfigPath(); + theKerberosProxySPN = theRobot->kerberosProxySPN(); + theKerberosServerSPN = theRobot->kerberosServerSPN(); theRobot->embedRecurRatio(theEmbedRecurRatio); theRobot->abortProb(theAbortProb); theRobot->authError(theAuthError); diff -ruBEN polygraph-4.3.1/src/client/CltCfg.cc.orig polygraph-4.3.1-mm/src/client/CltCfg.cc.orig --- polygraph-4.3.1/src/client/CltCfg.cc.orig 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.3.1-mm/src/client/CltCfg.cc.orig 2011-02-09 02:44:45.000000000 +0000 @@ -0,0 +1,869 @@ + +/* Web Polygraph http://www.web-polygraph.org/ + * (C) 2003-2006 The Measurement Factory + * Licensed under the Apache License, Version 2.0 */ + +#include "base/polygraph.h" + +#include +#include "xstd/h/iomanip.h" + +#include "xstd/rndDistrs.h" +#include "xstd/StringIdentifier.h" +#include "xstd/Ssl.h" +#include "base/RndPermut.h" +#include "base/OidGenStat.h" +#include "pgl/RobotSym.h" +#include "pgl/SessionSym.h" +#include "pgl/AclSym.h" +#include "pgl/GoalSym.h" +#include "pgl/PglStringSym.h" +#include "pgl/PglStaticSemx.h" +#include "pgl/SingleRangeSym.h" +#include "pgl/MultiRangeSym.h" +#include "runtime/AddrMap.h" +#include "runtime/HostMap.h" +#include "runtime/PubWorld.h" +#include "runtime/PopModel.h" +#include "runtime/Goal.h" +#include "runtime/XactAbortCoord.h" +#include "runtime/LogComment.h" +#include "runtime/httpHdrs.h" +#include "csm/XmlTagIdentifier.h" +#include "csm/ContentMgr.h" +#include "csm/ContentSel.h" +#include "client/WarmupPlan.h" +#include "client/ServerRep.h" +#include "client/RegExGroup.h" +#include "client/ForeignWorld.h" +#include "client/Client.h" +#include "client/CltCfg.h" +#include "client/CltOpts.h" +#include "client/SingleRangeCfg.h" +#include "client/MultiRangeCfg.h" + +Memberships CltCfg::TheGlbMemberships; + + +CltCfg::CltCfg(): theRobot(0), + theOriginSel(0), theReqTypeSel(0), theReqMethodSel(0), + thePopModel(0), + theBusyPeriod(0), theIdlePeriodDur(0), + theFtpProxyCycleCnt(0), theHttpProxyCycleCnt(0), theSocksProxyCycleCnt(0), + thePipelineDepth(0), + theRecurRatio(-1), theSpnegoAuthRatio(-1), theEmbedRecurRatio(-1), + theAbortProb(0), theAuthError(0), + theWaitXactLmt(-1), theIcpPort(-1), + theCredentialCycleCnt(0), theForeignWorld(0), + theContainerTags(0), + theAcceptedContentCodings(0), acceptingGzipContent(false), + theCookiesKeepLimitSel(0), + theWarmupPlan(0), + theReqBodyPauseProb(-1), + theReqBodyRecurrence(-1), + thePostContentSel(0), theUploadContentSel(0), + theForeignInterestProb(-1), + thePublicInterestProb(-1), + thePassiveFtp(-1), theSocksProb(-1), theSocksChainingProb(-1), + genUniqUrls(false), didWarmup(false), theRangeSel(0), + ftpProxiesSet(false) { +} + +CltCfg::~CltCfg() { + while (theCredentials.count()) + delete theCredentials.dequeue(); + while (theFtpProxies.count()) + delete theFtpProxies.dequeue(); + while (theHttpProxies.count()) + delete theHttpProxies.dequeue(); + while (theSocksProxies.count()) + delete theSocksProxies.dequeue(); + delete theOriginSel; + delete theReqTypeSel; + delete theReqMethodSel; + delete thePopModel; + delete theBusyPeriod; + delete theContainerTags; + delete theAcceptedContentCodings; + delete theForeignWorld; + delete theWarmupPlan; + delete thePostContentSel; + delete theUploadContentSel; + delete theRangeSel; +} + +void CltCfg::configure(const RobotSym *aRobot) { + AgentCfg::configure(aRobot); + + Assert(!theRobot && aRobot); + theRobot = aRobot; + + theRobot->waitXactLimit(theWaitXactLmt); + thePeerHttp = theRobot->peerHttp(); + thePeerIcp = theRobot->peerIcp(); + theRobot->icpPort(theIcpPort); + + theRobot->recurRatio(theRecurRatio); + theRobot->spnegoRatio(theSpnegoAuthRatio); + theRobot->embedRecurRatio(theEmbedRecurRatio); + theRobot->abortProb(theAbortProb); + theRobot->authError(theAuthError); + theRobot->uniqueUrls(genUniqUrls); + theUriThrower = theRobot->rawUriThrower(); + theRobot->reqBodyPauseProb(theReqBodyPauseProb); + theRobot->reqBodyPauseStart(theReqBodyPauseStart); + theRobot->reqBodyRecurrence(theReqBodyRecurrence); + + if (PopModelSym *pms = theRobot->popModel()) { + thePopModel = new PopModel; + thePopModel->configure(pms); + } + + configureInterests(); + configureReqTypes(); + configureReqMethods(); + configureOrigins(); + configureProxies(); + configureCredentials(); + configureMemberships(); + configureContainerTags(); + configureAcceptedContentCodings(); + configurePipeDepth(); + configureRanges(); + configurePostContents(); + configureUploadContents(); + configureTrace(); + + if (AclSym *acl = theRobot->acl()) + theAcl.configure(*acl); + + if (SessionSym *ss = theRobot->session()) { + if (GoalSym *bps = ss->busyPeriod()) { + theBusyPeriod = new Goal; + theBusyPeriod->configure(*bps); + } + theIdlePeriodDur = ss->idlePeriodDuration(); + ss->heartbeatGap(theSessionHeartbitGap); + + if (!theBusyPeriod != !theIdlePeriodDur) { + cerr << theRobot->loc() << "both busy and idle periods " + << "should be specified (or not specified) for a robot session" + << endl << xexit; + } + } + + if (!thePopModel && theRecurRatio > 0) + cerr << theRobot->loc() << "popularity model must be specified for" + << " positive recurrence ratio (robot " << theRobot->kind() << ')' + << endl << xexit; + + if (!theRobot->cookieSender(theCookieSenderProb)) + theCookieSenderProb = 1; + theCookiesKeepLimitSel = theRobot->cookiesKeepLimit(); + + if (theReqBodyPauseProb >= 0 && + theReqBodyPauseStart >= 0) + cerr << theRobot->loc() << "Both req_body_pause_prob and req_body_pause_start" + << " should not be specified (robot " << theRobot->kind() << ')' + << endl << xexit; + + theRobot->passiveFtp(thePassiveFtp); + + checkConfiguration(); +} + +int CltCfg::viservLimit() const { + return TheHostMap->iterationCount(); +} + +void CltCfg::configureInterests() { + Array istrs; + Array iprobs; + if (theRobot->interests(istrs, iprobs)) { + double pubProb = 0; + double privProb = 0; + for (int i = 0; i < istrs.count(); ++i) + if (istrs[i]->val() == "foreign") + theForeignInterestProb = iprobs[i]; + else + if (istrs[i]->val() == "public") + pubProb = iprobs[i]; + else + if (istrs[i]->val() == "private") + privProb = iprobs[i]; + else { + cerr << theRobot->loc() << "interests array can " + "have only \"foreign\", \"private\" and " + "\"public\" values" << endl << xexit; + } + if (privProb > 0) { + thePublicInterestProb = pubProb / (privProb + pubProb); + Comment(1) << theRobot->loc() << "private Robot URLs are " + "currently not supported, private interest is " + "treated as additional public interest" << endc; + } else + if (pubProb > 0) + thePublicInterestProb = 1; + // XXX: private worlds are not supported for now + thePublicInterestProb = 1; + } +} + +void CltCfg::configureReqTypes() { + static StringIdentifier sidf; + if (!sidf.count()) { + sidf.add("Basic", Client::rqtBasic); + sidf.add("Ims200", Client::rqtIms200); + sidf.add("Ims304", Client::rqtIms304); + sidf.add("Reload", Client::rqtReload); + sidf.add("Range", Client::rqtRange); + sidf.add("Upload", Client::rqtUpload); + } + + theReqTypeSel = theRobot->msgTypes(sidf); + if (!theReqTypeSel) + theReqTypeSel = new ConstDistr(0, Client::rqtBasic); // default + theReqTypeSel->rndGen(LclRndGen("client_req_types")); +} + +void CltCfg::configurePipeDepth() { + thePipelineDepth = theRobot->pipelineDepth(); + if (thePipelineDepth) + thePipelineDepth->rndGen(LclRndGen("client_pipe_depth")); +} + +void CltCfg::configureReqMethods() { + static StringIdentifier sidf; + if (!sidf.count()) { + sidf.add("GET", Client::rqmGet); + sidf.add("HEAD", Client::rqmHead); + sidf.add("POST", Client::rqmPost); + sidf.add("PUT", Client::rqmPut); + } + + theReqMethodSel = theRobot->reqMethods(sidf); + if (!theReqMethodSel) + theReqMethodSel = new ConstDistr(0, Client::rqmGet); // default + theReqMethodSel->rndGen(LclRndGen("client_req_methods")); +} + +void CltCfg::configureOrigins() { + PtrArray origNames; + + RndDistr *iad = 0; + if (!theRobot->origins(origNames, theOriginSel) || !origNames.count()) { + const bool isActive = !theRobot->reqInterArrival(iad) || iad; + if (isActive && theForeignInterestProb < 1) + Comment(0) << theRobot->loc() << "no origin addresses " + "specified for active Robot '" << + theRobot->kind() << "' with some non-foreign " + "interest" << endc << xexit; + return; + } + if (theForeignInterestProb >= 1) { + Comment(0) << theRobot->loc() << "origin addresses specified " + "for Robot '" << theRobot->kind() << "' with " + "foreign-only interest" << endc; + delete theOriginSel; + theOriginSel = 0; + return; + } + + Assert(theOriginSel); + theOriginSel->rndGen(LclRndGen("client_origins")); + + theViservs.stretch(origNames.count()); + for (int i = 0; i < origNames.count(); ++i) + addOrigName(*origNames[i]); +} + +// converts origin name into viserv idx +void CltCfg::addOrigName(const NetAddr &oname) { + if (!TheAddrMap->has(oname)) { + if (oname.isDomainName()) + cerr << here << "visible server name " << oname + << " is not found in address maps" << endl << xexit; + TheAddrMap->add(oname); + } + + int viserv = -1; + if (!TheHostMap->find(oname, viserv)) + TheHostMap->addAt(viserv, oname); + HostCfg *host = TheHostMap->at(viserv); + if (!host->thePubWorld) + PubWorld::Add(host, new PubWorld(UniqId::Create())); + + if (!host->theServerRep) + host->theServerRep = new ServerRep(oname, viserv); + + // quit if an origin entry is repeated because it complicates + // private world accounting (two origins would have one world) + if (hasViserv(viserv)) { + Comment << theRobot->loc() << "error: origin " << oname << + " is listed more than once in robot's origins" << + endc << xexit; + } + + theViservs.append(viserv); + + checkTargets(viserv); +} + +// checks content cfg and creates new server representative for viserv +void CltCfg::checkTargets(int viserv) { + const NetAddr &visName = TheHostMap->at(viserv)->theAddr; + int niamIdx; // name in AddrMap index + Assert(TheAddrMap->find(visName, niamIdx)); + + for (AddrMapAddrIter i = TheAddrMap->addrIter(niamIdx); i; ++i) { + const NetAddr &addr = i.addr(); + int targetIdx = -1; + if (!TheHostMap->find(addr, targetIdx) || + !TheHostMap->at(targetIdx)->theContent) { + Comment << theRobot->loc() << "error: Robot cannot find" + << " configuration for server@" << addr; + if (addr != i.name()) + Comment << " (visible as " << i.name() << ")"; + Comment << endc << xexit; + } + } +} + +void CltCfg::checkConfiguration() const { + // collect reachable contents + ContentSet contents; + for (int i = 0; i < theViservs.count(); ++i) { + const NetAddr &visName = TheHostMap->at(theViservs[i])->theAddr; + int niamIdx; // name in AddrMap index + Assert(TheAddrMap->find(visName, niamIdx)); + for (AddrMapAddrIter j = TheAddrMap->addrIter(niamIdx); j; ++j) { + int targetIdx = -1; + Assert(TheHostMap->find(j.addr(), targetIdx)); + const HostCfg *const hcfg = TheHostMap->at(targetIdx); + Assert(hcfg && hcfg->theContent); + const Array &srvContents = + hcfg->theContent->contents(); + for (int k = 0; k < srvContents.count(); ++k) + contents.insert(srvContents[k]); + } + } + + checkPostContentsConfiguration(contents); + checkUploadContentsConfiguration(contents); + checkRangesConfiguration(contents); +} + +void CltCfg::checkPostContentsConfiguration(const ContentSet &contents) const { + const bool havePostContents = thePostContentSel; + if (havePostRequest()) { + if (!havePostContents) { + Comment(0) << theRobot->loc() << "'POST' request method" + " configured but no 'post_contents' set" + << endc << xexit; + } + return; + } + bool noPostRequest = true; + for (ContentSet::const_iterator i = contents.begin(); + i != contents.end(); ++i) { + const ContentCfg *const content = *i; + Assert(content); + const CltCfg *const behavior = + TheCltBehaviorCfgs.get(content->id()); + if (!behavior || !behavior->havePostRequest()) + continue; + if (!havePostContents) { + Comment(0) << behavior->theRobot->loc() << "'POST' " + "request method configured for Content '" << + content->kind() << "' but Robot '" << + theRobot->kind() << "' has no 'post_contents' " + "set" << endc << xexit; + } + noPostRequest = false; + } + if (havePostContents && noPostRequest) { + Comment(0) << theRobot->loc() << "'post_contents' set but no " + "'POST' request method configured" << endl << xexit; + } +} + +void CltCfg::checkUploadContentsConfiguration(const ContentSet &contents) const { + const bool haveUploadContents = theUploadContentSel; + if (haveUploadRequest()) { + if (!haveUploadContents) { + Comment(0) << theRobot->loc() << "'Upload' request " + "type or 'PUT' request method configured but no" + " 'upload_contents' set" << endl << xexit; + } + return; + } + bool noUploadRequest = true; + for (ContentSet::const_iterator i = contents.begin(); + i != contents.end(); ++i) { + const ContentCfg *const content = *i; + Assert(content); + const CltCfg *const behavior = + TheCltBehaviorCfgs.get(content->id()); + if (!behavior || !behavior->haveUploadRequest()) + continue; + if (!haveUploadContents) { + Comment(0) << behavior->theRobot->loc() << "'Upload' " + "request type or 'PUT' request method " + "configured for Content '" << content->kind() + << "' but Robot '" << theRobot->kind() << "' " + "has no 'upload_contents' set" << endc << xexit; + } + noUploadRequest = false; + } + if (haveUploadContents && noUploadRequest) { + Comment(0) << theRobot->loc() << "'upload_contents' set but no " + "'Upload' request type or 'PUT' request method " + "configured" << endl << xexit; + } +} + +void CltCfg::checkRangesConfiguration(const ContentSet &contents) const { + const bool haveRanges = theRangeSel; + if (haveRangeRequest() && haveRanges) + return; + bool noRangeRequest = true; + bool allRanges = true; + for (ContentSet::const_iterator i = contents.begin(); + i != contents.end(); ++i) { + const ContentCfg *const content = *i; + Assert(content); + const CltCfg *const behavior = + TheCltBehaviorCfgs.get(content->id()); + if (!behavior) { + allRanges = false; + continue; + } + const bool needRanges = behavior->theReqTypeSel ? + behavior->haveRangeRequest() : haveRangeRequest(); + if (needRanges && !behavior->theRangeSel) + allRanges = false; + if (behavior->theRangeSel && !needRanges) { + Comment(0) << behavior->theRobot->loc() << + "'ranges' set but no 'Range' request type " + "configured for Content '" << content->kind() << + "' and Robot '" << theRobot->kind() << '\'' + << endl << xexit; + } + if (!haveRanges && !behavior->theRangeSel && needRanges) { + Comment(0) << behavior->theRobot->loc() << "'Range' " + "request type configured but no 'ranges' set " + "for Content '" << content->kind() << "' and " + "Robot '" << theRobot->kind() << '\'' + << endc << xexit; + } + if (behavior->haveRangeRequest()) + noRangeRequest = false; + } + if (haveRanges && noRangeRequest) { + Comment(0) << theRobot->loc() << "'ranges' set but no 'Range' " + "request type configured" << endl << xexit; + } + if (haveRangeRequest() && !allRanges) { + Comment(0) << theRobot->loc() << "'Range' request " + "type configured but no 'ranges' set" + << endl << xexit; + } +} + +bool CltCfg::havePostRequest() const { + return theRobot->arrayHasElem("req_methods", "POST"); +} + +bool CltCfg::haveUploadRequest() const { + return theRobot->arrayHasElem("req_types", "Upload") || + theRobot->arrayHasElem("req_methods", "PUT"); +} + +bool CltCfg::haveRangeRequest() const { + return theRobot->arrayHasElem("req_types", "Range"); +} + +void CltCfg::configureProxies() { + Array addrs; + + ftpProxiesSet = theRobot->ftpProxies(addrs); + if (ftpProxiesSet) { + theFtpProxies.resize(addrs.count()); + while (addrs.count()) + theFtpProxies.enqueue(addrs.pop()); + } + + if (theRobot->httpProxies(addrs)) { + theHttpProxies.resize(addrs.count()); + while (addrs.count()) + theHttpProxies.enqueue(addrs.pop()); + if (TheCltOpts.theProxyAddr) + Comment << theRobot->loc() << "--proxy option and " + << "Robot.http_proxies field are mutually exclusive" << endc << xexit; + } + + if (theRobot->proxies(addrs)) { + Comment(1) << theRobot->loc() << "Robot::proxies field is " + << "deprecated in favor of the Robot::http_proxies " + << "field. See also Robot::ftp_proxies" << endc; + if (!theHttpProxies.empty()) + Comment << theRobot->loc() << "Robot.http_proxies and " + << "Robot.proxies field are mutually exclusive" << endc << xexit; + if (TheCltOpts.theProxyAddr) + Comment << theRobot->loc() << "--proxy option and " + << "Robot.proxies field are mutually exclusive" << endc << xexit; + theHttpProxies.resize(addrs.count()); + while (addrs.count()) + theHttpProxies.enqueue(addrs.pop()); + } + + configureSocksProxies(); +} + +void CltCfg::configureSocksProxies() { + Array addrs; + const bool socksProbDefault = !theRobot->socksProb(theSocksProb); + if (theRobot->socksProxies(addrs)) { + theSocksProxies.resize(addrs.count()); + while (addrs.count()) + theSocksProxies.enqueue(addrs.pop()); + if (socksProbDefault) + theSocksProb = 1; + } else if (theSocksProb > 0) { + cerr << theRobot->loc() << "Robot::socks_prob field " + << "is positive but no Robot::socks_proxies configured" + << endl << xexit; + } + const bool socksProxyPresent = !theSocksProxies.empty(); + const bool otherProxyPresent = + !theHttpProxies.empty() || !theFtpProxies.empty(); + theRobot->socksChainingProb(theSocksChainingProb); + if (theSocksChainingProb > 0) { + if (!socksProxyPresent) { + cerr << theRobot->loc() + << "Robot::socks_chaining_prob field is positive " + << "but no Robot::socks_proxies configured" + << endl << xexit; + } + if (!otherProxyPresent) { + cerr << theRobot->loc() + << "Robot::socks_chaining_prob field is positive " + << "but no Robot::http_proxies/Robot::ftp_proxies " + << "configured" << endl << xexit; + } + } +} + +void CltCfg::configureCredentials() { + Array creds; + theRobot->credentials(creds); + theCredentials.resize(creds.count()); + while (creds.count()) + theCredentials.enqueue(creds.pop()); +} + +void CltCfg::configureContainerTags() { + Array tags; + if (!theRobot->containerTags(tags)) + tags.append(new String("")); // default + theContainerTags = new XmlTagIdentifier(); + theContainerTags->configure(tags); + while (tags.count()) delete tags.pop(); +} + +void CltCfg::configureAcceptedContentCodings() { + Array codings; + if (theRobot->acceptedContentCodings(codings)) { + theAcceptedContentCodings = new String; + for (int i = 0; i < codings.count(); ++i) { + const String &coding = *codings[i]; + if (coding.startsWith("*") || coding.startsWith("gzip")) + acceptingGzipContent = true; + if (*theAcceptedContentCodings) + *theAcceptedContentCodings += ", "; + *theAcceptedContentCodings += coding; + delete codings[i]; + } + } +} + +void CltCfg::configureMemberships() { + // initialize global array once + const Array &syms = PglStaticSemx::TheMembershipsToUse; + if (TheGlbMemberships.count() != syms.count()) { + TheGlbMemberships.stretch(syms.count()); + for (int i = 0; i < syms.count(); ++i) { + MembershipMap *m = new MembershipMap; + m->configure(*syms[i], i+1); + TheGlbMemberships.append(m); + } + } + + for (int i = 0; i < TheGlbMemberships.count(); ++i) { + MembershipMap *g = TheGlbMemberships[i]; + bool belongs = false; + for (int u = 0; !belongs && u < theCredentials.count(); ++u) { + belongs = g->hasMember(*theCredentials.top(u)); + } + if (belongs) + theLclMemberships.append(g); + } +} + +void CltCfg::configureTrace() { + if (const String fname = theRobot->foreignTrace()) { + if (theForeignInterestProb > 0) { + theForeignWorld = new ForeignWorld; + theForeignWorld->configure(fname); + } else { + Comment(1) << theRobot->loc() << "warning: foreign " + << "trace '" << fname << "' is configured " + << "but no foreign interest specified" + << endc; + } + } else + if (theForeignInterestProb > 0) { + Comment << theRobot->loc() << "error: foreign interest is " + << "positive but no foreign trace configured" + << endc << xexit; + } +} + +void CltCfg::configureRanges() { + Array syms; + if (!theRobot->ranges(syms, theRangeSel)) + return; // no ranges configured + + theRangeSel->rndGen(LclRndGen("client_ranges")); + theRanges.stretch(syms.count()); + for (int i = 0; i < syms.count(); ++i) { + const RangeSym &sym = *syms[i]; + if (sym.isA(SingleRangeSym::TheType)) { + SingleRangeCfg *const r = new SingleRangeCfg; + r->configure((const SingleRangeSym&)sym); + theRanges.append(r); + } + else + if (sym.isA(MultiRangeSym::TheType)) { + MultiRangeCfg *const r = new MultiRangeCfg; + r->configure((const MultiRangeSym&)sym); + theRanges.append(r); + } + else + cerr + << theRobot->loc() + << "Unknown range type \"" << sym.type() << '"' + << endl << xexit; + } +} + +void CltCfg::configurePostContents() { + Array syms; + if (!theRobot->reqContents("post_contents", syms, thePostContentSel)) + return; // no contents configured + + thePostContentSel->rndGen(LclRndGen("client_post_contents")); + thePostContents.stretch(syms.count()); + TheContentMgr.get(syms, thePostContents); +} + +void CltCfg::configureUploadContents() { + Array syms; + if (!theRobot->reqContents("upload_contents", syms, theUploadContentSel)) { + if (!theRobot->reqContents("put_contents", syms, theUploadContentSel)) + return; // no contents configured + else { + Comment(1) << theRobot->loc() << "Robot::put_contents field " + "is deprecated in favor of the " + "Robot::upload_contents field" << endc; + } + } + + theUploadContentSel->rndGen(LclRndGen("client_upload_contents")); + theUploadContents.stretch(syms.count()); + TheContentMgr.get(syms, theUploadContents); +} + +bool CltCfg::hasViserv(int viserv) const { + for (int origin = 0; origin < theViservs.count(); ++origin) { + if (theViservs[origin] == viserv) + return true; + } + return false; +} + +bool CltCfg::selectCredentials(String &newCred) { + if (theCredentials.empty()) + return false; + + // randomize in the beginning of each cycle + if (theCredentialCycleCnt >= theCredentials.count()) + theCredentialCycleCnt = 0; + if (!theCredentialCycleCnt) { + static RndGen rng; + theCredentials.randomize(rng); + } + theCredentialCycleCnt++; + + String *credential = theCredentials.dequeue(); + theCredentials.enqueue(credential); + newCred = *credential; + return true; +} + +int CltCfg::selectViserv() { + if (theWarmupPlan) { + const int viservIdx = theWarmupPlan->selectViserv(); + if (viservIdx >= 0) + return viservIdx; + stopWarmup(); + } + + const int viservIdx = (int)theOriginSel->trial(); + Assert(0 <= viservIdx && viservIdx < theViservs.count()); + return theViservs[viservIdx]; +} + +bool CltCfg::selectFtpProxy(NetAddr &newAddr) { + if (!ftpProxiesSet) + return false; + + selectProxy(theFtpProxies, theFtpProxyCycleCnt, newAddr); + return true; +} + +bool CltCfg::selectHttpProxy(NetAddr &newAddr) { + if (TheCltOpts.theProxyAddr) + newAddr = TheCltOpts.theProxyAddr; + else + selectProxy(theHttpProxies, theHttpProxyCycleCnt, newAddr); + return true; +} + +bool CltCfg::selectSocksProxy(NetAddr &newAddr, bool &doSocksChaining) { + if (theSocksProb <= 0) + return false; + + static RndGen *const rng1 = LclRndGen("socks_prob"); + if (!rng1->event(theSocksProb)) + return false; + + selectProxy(theSocksProxies, theSocksProxyCycleCnt, newAddr); + if (theSocksChainingProb > 0) { + static RndGen *const rng2 = LclRndGen("socks_chaining_prob"); + doSocksChaining = rng2->event(theSocksChainingProb); + } else + doSocksChaining = false; + return true; +} + +// XXX: merge this with SrvCfg::selectAbortCoord() +void CltCfg::selectAbortCoord(XactAbortCoord &coord) { + static RndGen rng1, rng2; // uncorrelated unless theAbortProb is 1 + if (rng1.event(theAbortProb)) { + const int whether = rng2.state(); + (void)rng2.trial(); + coord.configure(rng2.state(), whether); + } else { + const int whether = rng1.state(); + (void)rng1.trial(); + coord.configure(whether, rng1.state()); + } +} + +bool CltCfg::selectFtpMode(bool &usePassive) { + if (thePassiveFtp < 0) + return false; + + static RndGen rng; + usePassive = rng.event(thePassiveFtp); + return true; +} + +int CltCfg::findMemberships(const String &user, Memberships &groups) const { + if (user) { + for (int i = 0; i < theLclMemberships.count(); ++i) { + MembershipMap *g = theLclMemberships[i]; + if (g->hasMember(user)) + groups.append(g); + } + } + return groups.count(); +} + +bool CltCfg::followAllUris(const RepHdr &rep) const { + if (theUriThrower.len() > 0) { + return theUriThrower == "*" || + rep.theServer.startsWith(theUriThrower); + } else { + return false; + } +} + +RangeCfg::RangesInfo CltCfg::makeRangeSet(ostream &os, const ObjId &oid, ContentCfg &contentCfg) const { + Assert(theRangeSel); + return theRanges[theRangeSel->ltrial()]->makeRangeSet(os, oid, contentCfg); +} + +// may be called multiple times +void CltCfg::startWarmup() { + if (!theWarmupPlan && !didWarmup && theForeignInterestProb < 1) { + theWarmupPlan = new WarmupPlan(theViservs); + + Comment(7) << "started a warmup plan for Robot '" << + theRobot->kind() << "' with " << + theWarmupPlan->planCount() << " cold servers, out " + "of " << PubWorld::Count() << " already visible" << endc; + } +} + +void CltCfg::stopWarmup() { + Assert(theWarmupPlan && !didWarmup); + + Comment(7) << "stopped warmup plan for Robot '" << theRobot->kind() << + "' with " << theWarmupPlan->planCount() << " cold servers, " + "out of " << PubWorld::Count() << " globally visible" << endc; + + delete theWarmupPlan; + theWarmupPlan = 0; + didWarmup = true; +} + +void CltCfg::selectProxy(Ring &proxies, int &proxyCycleCnt, NetAddr &newAddr) { + if (proxies.empty()) + return; + + // randomize in the beginning of each cycle + if (proxyCycleCnt >= proxies.count()) + proxyCycleCnt = 0; + if (!proxyCycleCnt) { + static RndGen rng; + proxies.randomize(rng); + } + proxyCycleCnt++; + + NetAddr *addr = proxies.dequeue(); + proxies.enqueue(addr); + newAddr = *addr; +} + +/* CltSharedCfgs */ + +CltSharedCfgs::~CltSharedCfgs() { + while (count()) delete pop(); +} + +CltCfg *CltSharedCfgs::getConfig(const RobotSym *rs) { + for (int i = 0; i < count(); ++i) { + if (item(i)->theRobot == rs) + return item(i); + } + return addConfig(rs); +} + +CltCfg *CltSharedCfgs::addConfig(const RobotSym *rs) { + CltCfg *cfg = new CltCfg; + cfg->configure(rs); + append(cfg); + return cfg; +} diff -ruBEN polygraph-4.3.1/src/client/CltCfg.h polygraph-4.3.1-mm/src/client/CltCfg.h --- polygraph-4.3.1/src/client/CltCfg.h 2010-12-10 18:25:55.000000000 +0000 +++ polygraph-4.3.1-mm/src/client/CltCfg.h 2011-03-04 23:15:41.000000000 +0000 @@ -119,6 +119,11 @@ double thePublicRatio; // how often to request a "public" object double theRecurRatio; // how often to re-visit an old object double theSpnegoAuthRatio; // how often negotiate/spnego auth is preferred over NTLMSSP + bool preferKerberosAuth; // is negotiate/kerberos auth preferred over negotiate/spnego + bool clearKerberosCache; // clear cache before new session + String theKerberosConfigPath; // is the Kerberos configuration file location (krb5.conf) + String theKerberosProxySPN; // is the Kerberos service principal name for the proxy + String theKerberosServerSPN; // is the Kerberos service principal name for the server double theEmbedRecurRatio; // how often to re-visit an old embedded obj double theAbortProb; // prob of aborting a transaction double theAuthError; // prob of an authentication error diff -ruBEN polygraph-4.3.1/src/client/CltCfg.h.orig polygraph-4.3.1-mm/src/client/CltCfg.h.orig --- polygraph-4.3.1/src/client/CltCfg.h.orig 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.3.1-mm/src/client/CltCfg.h.orig 2010-12-10 18:25:55.000000000 +0000 @@ -0,0 +1,178 @@ + +/* Web Polygraph http://www.web-polygraph.org/ + * (C) 2003-2006 The Measurement Factory + * Licensed under the Apache License, Version 2.0 */ + +#ifndef POLYGRAPH__CLIENT_CLTCFG_H +#define POLYGRAPH__CLIENT_CLTCFG_H + +#include +#include "xstd/Ring.h" +#include "xstd/BigSize.h" +#include "xstd/NetAddr.h" +#include "runtime/AgentCfg.h" +#include "client/MembershipMap.h" +#include "client/AclGroup.h" +#include "client/RangeCfg.h" + +class Goal; +class ObjId; +class RndDistr; +class PopModel; +class RepHdr; +class RobotSym; +class WarmupPlan; +class XactAbortCoord; +class RegExGroup; +class ForeignWorld; +class XmlTagIdentifier; +class ContentCfg; + +// Client configuration items that can be shared among multiple clients +class CltCfg: public AgentCfg { + public: + CltCfg(); + ~CltCfg(); + + void configure(const RobotSym *aRobot); + + int viservLimit() const; + int viservCount() const { return theViservs.count(); } + bool hasViserv(int viserv) const; + + void startWarmup(); + void stopWarmup(); + + int selectViserv(); + bool selectFtpProxy(NetAddr &addr); + bool selectHttpProxy(NetAddr &addr); + bool selectSocksProxy(NetAddr &addr, bool &doSocksChaining); + bool selectCredentials(String &newName); + void selectAbortCoord(XactAbortCoord &coord); + bool selectFtpMode(bool &usePassive); + + bool followAllUris(const RepHdr &rep) const; + int findMemberships(const String &user, Memberships &memberships) const; + const AclGroup &acl() const { return theAcl; } + ForeignWorld *foreignWorld() { return theForeignWorld; } + + RangeCfg::RangesInfo makeRangeSet(ostream &os, const ObjId &oid, ContentCfg &contentCfg) const; + + protected: + void configureInterests(); + void configureReqTypes(); + void configureReqMethods(); + void configureOrigins(); + void configureProxies(); + void configureSocksProxies(); + void configureCredentials(); + void configureMemberships(); + void configureContainerTags(); + void configureAcceptedContentCodings(); + void configurePipeDepth(); + void configureTrace(); + void configureRanges(); + void configurePostContents(); + void configureUploadContents(); + + void addOrigName(const NetAddr &oname); + void checkTargets(int viserv); + + typedef std::set ContentSet; + void checkConfiguration() const; + void checkPostContentsConfiguration(const ContentSet &contents) const; + void checkUploadContentsConfiguration(const ContentSet &contents) const; + void checkRangesConfiguration(const ContentSet &contents) const; + bool havePostRequest() const; + bool haveUploadRequest() const; + bool haveRangeRequest() const; + + static void selectProxy(Ring &proxies, int &proxyCycleCnt, NetAddr &newAddr); + + protected: + static Memberships TheGlbMemberships; + + public: + const RobotSym *theRobot; // used to identify/share configs + + Array theViservs; // origin idx to HostMap idx (viserv) + RndDistr *theOriginSel; // selects origin idx + RndDistr *theReqTypeSel; // selects request types + RndDistr *theReqMethodSel; // selects request methods + PopModel *thePopModel; // popularity model to select old oids + + Goal *theBusyPeriod; // sessions's busy period config + RndDistr *theIdlePeriodDur;// sessions's idle period config + Time theSessionHeartbitGap;// hearbit interval + + Ring theFtpProxies; + Ring theHttpProxies; + Ring theSocksProxies; + int theFtpProxyCycleCnt; // to randomize proxy selection + int theHttpProxyCycleCnt; // to randomize proxy selection + int theSocksProxyCycleCnt; // to randomize proxy selection + + RndDistr *thePipelineDepth;// maximum concurrent pipelined requests + + NetAddr thePeerHttp; // caching peer talking HTTP + NetAddr thePeerIcp; // caching peer talking ICP + double thePublicRatio; // how often to request a "public" object + double theRecurRatio; // how often to re-visit an old object + double theSpnegoAuthRatio; // how often negotiate/spnego auth is preferred over NTLMSSP + double theEmbedRecurRatio; // how often to re-visit an old embedded obj + double theAbortProb; // prob of aborting a transaction + double theAuthError; // prob of an authentication error + + int theWaitXactLmt; // limit on the number of postponed requests + int theIcpPort; // IPC client should use this port + + Memberships theLclMemberships; // all user groups users belong to + Ring theCredentials; // all users under this cfg + int theCredentialCycleCnt; + AclGroup theAcl; + + String theUriThrower; // follow all URIs from this server + ForeignWorld *theForeignWorld; // foreign URLs trace, if any + + XmlTagIdentifier *theContainerTags; + String *theAcceptedContentCodings; // for Accept-Encoding header + bool acceptingGzipContent; // pre-parsed status + + RndDistr *theCookiesKeepLimitSel; // how many cookies to keep + + WarmupPlan *theWarmupPlan; + + double theReqBodyPauseProb; // how often to send "Expect: 100-continue" + BigSize theReqBodyPauseStart; // body size limt to send "Expect: 100-continue" + + double theReqBodyRecurrence; + Array thePostContents; + RndDistr *thePostContentSel; + Array theUploadContents; + RndDistr *theUploadContentSel; + + double theForeignInterestProb; + double thePublicInterestProb; + + double thePassiveFtp; // probability of a robot using passive FTP + double theSocksProb; // probability of a robot using SOCKS proxy + double theSocksChainingProb; // probability of a SOCKS-using robot also using HTTP/FTP proxies + + bool genUniqUrls; // each generated URL must be unique + bool didWarmup; // warmup plan was completed + Array theRanges; // range configurations + RndDistr *theRangeSel; // selects single range + + bool ftpProxiesSet; // true if Robot.ftp_proxies field is set +}; + +class CltSharedCfgs: protected Array { + public: + ~CltSharedCfgs(); + CltCfg *getConfig(const RobotSym *cfg); + + protected: + CltCfg *addConfig(const RobotSym *cfg); +}; + +#endif diff -ruBEN polygraph-4.3.1/src/client/GssKrbCtx.h polygraph-4.3.1-mm/src/client/GssKrbCtx.h --- polygraph-4.3.1/src/client/GssKrbCtx.h 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.3.1-mm/src/client/GssKrbCtx.h 2011-03-04 23:15:41.000000000 +0000 @@ -0,0 +1,78 @@ +#ifndef __HTTP_GSSKRBCTX_H +#define __HTTP_GSSKRBCTX_H + +#ifdef HAVE_KRB5_H +#ifdef HAVE_BROKEN_HEIMDAL_KRB5_H +extern "C" { +#include +} +#else +#include +#endif +#endif /* HAVE_KRB5_H */ + +#ifdef HAVE_GSSAPI_H +#include +#elif defined(HAVE_GSSAPI_GSSAPI_H) +#include +#endif /* HAVE_GSSAPI_H */ + +#ifndef gss_nt_service_name +#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE +#endif + +#if !defined(HAVE_HEIMDAL_KERBEROS) +#ifdef HAVE_GSSAPI_GSSAPI_EXT_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_EXT_H */ +#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */ +#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_GENERIC_H */ +#endif /* !HAVE_HEIMDAL_KERBEROS */ + +class GssKrbXact { + public: + GssKrbXact(); + OM_uint32 gssMajorStatus; + OM_uint32 gssMinorStatus; + gss_name_t gssServerName; + void gssCleanup(); + int checkGssError(String gssFunctionName); + NtlmAuthState NegoKerberosAuthPrint(ostream &os, String gssBase64Token); + static int pton(const char *p); + + class KrbEnv { + public: + KrbEnv(); + ~KrbEnv(); + String krbUserp; + String krbPasswdp; + String krbServerName; + String krbCfgPath; + String krbSPN; + bool clearKrbCc; + NtlmAuthState krbSetup(); + + private: + krb5_context theKrbCtx; + krb5_principal theKrbClt; + krb5_ccache theKrbCc; + krb5_creds theKrbCreds; + krb5_error_code theKrbErrorCode; + struct krbCtxStruct { + String theKrbCfgPath; + krb5_context theKrbCtx; + struct krbCtxStruct *theNextKrbCtx; + } *theKrbCtxStruct; + NtlmAuthState krbCleanup(); + int krbCheckError(String theKrbFunctionName); + }; + friend class KrbEnv; + + private: + gss_ctx_id_t theGssContext; +}; +#endif diff -ruBEN polygraph-4.3.1/src/client/HttpCltXact.cc polygraph-4.3.1-mm/src/client/HttpCltXact.cc --- polygraph-4.3.1/src/client/HttpCltXact.cc 2010-12-22 23:42:25.000000000 +0000 +++ polygraph-4.3.1-mm/src/client/HttpCltXact.cc 2011-03-04 23:15:41.000000000 +0000 @@ -16,6 +16,7 @@ #include "client/MarkupBodyParser.h" #include "client/MultiPartParser.h" #include "client/NtlmAuth.h" +#include "client/KerberosAuth.h" #include "client/SingleCxm.h" #include "client/PipelinedCxm.h" #include "client/PrivCache.h" @@ -586,16 +587,17 @@ void HttpCltXact::makeProxyAuthHdr(ostream &os) { const HttpAuthScheme scheme(theOwner->proxyAuthScheme(theOid)); Connection::NtlmAuth &ntlmState(theConn->theProxyNtlmState); - makeAuthHdr(hfpProxyAuthorization, scheme, ntlmState, os); + makeAuthHdr(true, hfpProxyAuthorization, scheme, ntlmState, os); } void HttpCltXact::makeOriginAuthHdr(ostream &os) { const HttpAuthScheme scheme(theOwner->originAuthScheme(theOid)); Connection::NtlmAuth &ntlmState(theConn->theOriginNtlmState); - makeAuthHdr(hfpAuthorization, scheme, ntlmState, os); + makeAuthHdr(false, hfpAuthorization, scheme, ntlmState, os); } -void HttpCltXact::makeAuthHdr(const String &header, const HttpAuthScheme scheme, Connection::NtlmAuth &ntlmState, ostream &os) { +void HttpCltXact::makeAuthHdr(const bool proxyAuth, const String &header, const HttpAuthScheme scheme, + Connection::NtlmAuth &ntlmState, ostream &os) { theOid.authCred(false); switch (ntlmState.state) { case ntlmNone: { // Initiating auth @@ -607,9 +609,37 @@ } else if (scheme == authNegotiate) { makeAuthorization(header, scheme, os); - NegoNtlmAuthPrintT1(os, ntlmState.useSpnegoNtlm); - os << crlf; - ntlmState.state = ntlmSentT1; + if (!theOwner->cfg()->preferKerberosAuth) { // Spnego + NegoNtlmAuthPrintT1(os, ntlmState.useSpnegoNtlm); + os << crlf; + ntlmState.state = ntlmSentT1; + } else { // Kerberos otherwise + if (theOwner->credentialsFor(theOid, theCred)) { + Area aUser = theCred.name(); + String sUser(aUser.data(), aUser.size()); + Area aPass = theCred.password(); + String sPass(aPass.data(), aPass.size()); + if (proxyAuth) { + ntlmState.state = + CltGssXact::NegoKerberosAuthPrintT1(os, sUser, + sPass, theOwner->proxy(theOid).addrA(), + theOwner->cfg()->theKerberosConfigPath, + theOwner->cfg()->clearKerberosCache, + theOwner->cfg()->theKerberosProxySPN); + } else {// Oid2UrlHost(theOid).addrA() should have the name but + // has only the IP. need to look for right lookup call + ntlmState.state = + CltGssXact::NegoKerberosAuthPrintT1(os, sUser, + sPass, Oid2UrlHost(theOid).addrA(), + theOwner->cfg()->theKerberosConfigPath, + theOwner->cfg()->clearKerberosCache, + theOwner->cfg()->theKerberosServerSPN); + } + os << crlf; + } else { + ntlmState.state = kerberosError; + } + } } else if (scheme == authBasic) { if (theOwner->credentialsFor(theOid, theCred)) { @@ -623,6 +653,15 @@ } break; } + case kerberosSentT1: { + if (!header.cmp(hfpProxyAuthorization)) { + ntlmState.state = CltGssXact::NegoKerberosAuthPrintT3(os, ntlmState.hdrRcvdT2); + } else { + ntlmState.state = CltGssXact::NegoKerberosAuthPrintT3(os, ntlmState.hdrRcvdT2); + } + os << crlf; + break; + } case ntlmSentT1: { if (theOwner->credentialsFor(theOid, theCred)) { Area aUser = theCred.name(); @@ -646,7 +685,12 @@ } break; } + case kerberosDone: case ntlmDone: + case kerberosError: { + // do nothing + break; + } case ntlmError: { // do nothing break; @@ -943,21 +987,26 @@ authError = startAuth(authNtlm); } else if (challenge.scheme == authNegotiate) { - ntlmState.useSpnegoNtlm = isSpnegoNtlm(challenge.params.cstr()); - if (ntlmState.useSpnegoNtlm && theOwner->cfg()->theSpnegoAuthRatio >= 0) { - static RndGen rng; - ntlmState.useSpnegoNtlm = rng.event(theOwner->cfg()->theSpnegoAuthRatio); - } + if (!theOwner->cfg()->preferKerberosAuth) { // Spnego + ntlmState.useSpnegoNtlm = isSpnegoNtlm(challenge.params.cstr()); + if (ntlmState.useSpnegoNtlm && theOwner->cfg()->theSpnegoAuthRatio >= 0) { + static RndGen rng; + ntlmState.useSpnegoNtlm = rng.event(theOwner->cfg()->theSpnegoAuthRatio); + } - // According to rfc4559 spnego-based negotiate auth is not supposed - // to be used by proxies (but can be passed transparently to servers), - // so we can't really say whether we need to retry here or what. Need - // to test on available clients and proxies. - doRetry = ntlmState.useSpnegoNtlm; + // According to rfc4559 spnego-based negotiate auth is not supposed + // to be used by proxies (but can be passed transparently to servers), + // so we can't really say whether we need to retry here or what. Need + // to test on available clients and proxies. + doRetry = ntlmState.useSpnegoNtlm; - // sent nothing, in case of NTLMSSP proxy should signal disconnect, - // Negotiate authentication will resume once we reconnect - authError = startAuth(authNegotiate); + // sent nothing, in case of NTLMSSP proxy should signal disconnect, + // Negotiate authentication will resume once we reconnect + authError = startAuth(authNegotiate); + } else { // Kerberos otherwise + doRetry = true; + authError = startAuth(authNegotiate); + } } else if (!theCred.image()) { // sent nothing, Basic auth @@ -970,6 +1019,11 @@ // else auth correctly denied due to invalid credentials break; } + case kerberosSentT1: { + ntlmState.hdrRcvdT2 = challenge.params; + doRetry = true; // continue processing in HopByHops + break; + } case ntlmSentT1: { ntlmState.hdrRcvdT2 = challenge.params; doRetry = true; // continue processing in HopByHops @@ -1000,6 +1054,14 @@ authError = proxyAuth ? errProxyAuthAllowed : errOriginAuthAllowed; break; } + case kerberosSentT1: { // as expected + ntlmState.state = kerberosDone; + break; + } + case kerberosDone: { + // do nothing, everything is ok + break; + } case ntlmDone: { // do nothing, everything is ok break; diff -ruBEN polygraph-4.3.1/src/client/HttpCltXact.cc.orig polygraph-4.3.1-mm/src/client/HttpCltXact.cc.orig --- polygraph-4.3.1/src/client/HttpCltXact.cc.orig 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.3.1-mm/src/client/HttpCltXact.cc.orig 2010-12-22 23:42:25.000000000 +0000 @@ -0,0 +1,1437 @@ + +/* Web Polygraph http://www.web-polygraph.org/ + * (C) 2003-2006 The Measurement Factory + * Licensed under the Apache License, Version 2.0 */ + +#include "base/polygraph.h" + +#include "base/StatIntvlRec.h" +#include "client/AnyBodyParser.h" +#include "client/Client.h" +#include "client/CltCfg.h" +#include "client/CltDataFilterRegistry.h" +#include "client/CltOpts.h" +#include "client/ChunkedCodingParser.h" +#include "client/HttpCltXact.h" +#include "client/MarkupBodyParser.h" +#include "client/MultiPartParser.h" +#include "client/NtlmAuth.h" +#include "client/SingleCxm.h" +#include "client/PipelinedCxm.h" +#include "client/PrivCache.h" +#include "client/RegExGroups.h" +#include "client/ServerRep.h" +#include "client/UriScriptBodyParser.h" +#include "csm/BodyIter.h" +#include "csm/ContentCfg.h" +#include "csm/oid2Url.h" +#include "runtime/globals.h" +#include "runtime/httpText.h" +#include "runtime/polyErrors.h" +#include "runtime/CompoundXactInfo.h" +#include "runtime/ErrorMgr.h" +#include "runtime/Farm.h" +#include "runtime/HostMap.h" +#include "runtime/HttpCookies.h" +#include "runtime/HttpDate.h" +#include "runtime/LogComment.h" +#include "runtime/PageInfo.h" +#include "runtime/PubWorld.h" +#include "runtime/SharedOpts.h" +#include "runtime/StatPhaseMgr.h" +#include "runtime/StatPhaseSync.h" + + +HttpCltXact::HttpCltXact() { + HttpCltXact::reset(); +} + +void HttpCltXact::reset() { + CltXact::reset(); + + theRepHdr.reset(); + theOlcTimes.reset(); + theReqBodySize = -1; + thePeerState = peerUnknown; + theSavedRepHeader.reset(); + the100ContinueState = csNone; + + theRangesSize = Size(); + theRangeCount = Size(); + theBodyPartsSize = 0; + theBodyPartCount = 0; + + theActualRepType = -1; + + theReqFlags = 0; +} + +HttpAuthScheme HttpCltXact::proxyAuth() const { + Assert(theOwner); + return theOwner->proxyAuthScheme(theOid); +} + +PipelinedCxm *HttpCltXact::getPipeline() { + Assert(theMgr); + + if (!theConn->pipelineable()) + return 0; + + // if we are not already pipelining, create a pipeline manager + if (!theMgr->pipelining()) { + Assert(theState == stBodyWaiting); + PipelinedCxm *mgr = PipelinedCxm::Get(); + mgr->assumeReadControl(this, theMgr); + theMgr->release(this); + theMgr = mgr; + return mgr; + } + + return dynamic_cast(theMgr); +} + +void HttpCltXact::pipeline(PipelinedCxm *aMgr) { + Assert(theMgr != aMgr); // or we will overincrement useLevel + theMgr->release(this); + aMgr->join(this); // we also call control() in exec() + theMgr = aMgr; + theConn = theMgr->conn(); + Assert(theConn); + theConn->startUse(); +} + +void HttpCltXact::exec(Connection *const aConn) { + CltXact::exec(aConn); + + theOwner->cfg()->selectAbortCoord(theAbortCoord); + + Should (!theConn->tunneling() || theConn->sslConfigured()); + if (theConn->sslConfigured() && !theConn->sslActive()) { + if (theConn->tunneling()) + theOid.connect(true); + else + theConn->sslActivate(); + } + + if (!theMgr) + theMgr = SingleCxm::Get(); // may be changed to PipelinedCxm later + theMgr->control(this); + + newState(stConnWaiting); +} + +void HttpCltXact::finish(Error err) { + if (!err && theRepHdr.redirect()) + redirect(); + + // special actions for objects with bodies + if (!theOid.head() && theBodyParser && theBodyParser->used()) { + // check MD5 + if (!err && !theOid.aborted() && theRepHdr.theChecksum.set()) { + theCheckAlg.final(); + if (!theCheckAlg.sum().equal(theRepHdr.theChecksum)) { + if (ReportError(errChecksum)) { + theRepHdr.theChecksum.print(Comment << "MD5 expected: ") << endc; + theCheckAlg.sum().print(Comment << "MD5 received: ") << endc; + if (TheOpts.theDumpFlags(dumpErr, dumpAny)) + printMsg(theConn->theRdBuf); + } + err = errOther; + } + } + + // cache + if (!err && theOid.cachable()) { + if (PrivCache *cache = theOwner->privCache()) + cache->storeOid(theOid); + } + } + + if (err && theOwner->privCache()) + theOwner->privCache()->purgeOid(theOid); + + if (!theMgr) { + // no manager if lifetime timeout happens while in waiting queue + Must(err); + Must(err == errXactLifeTimeExpired); + } else if (err) { + if (err != errPipelineAbort) + theMgr->noteAbort(this); + } else { + theMgr->noteDone(this); + } + + if (theAuthXact) { + ++theAuthXact->exchanges; + theAuthXact->reqSize += theReqSize.actual(); + theAuthXact->repSize += theRepSize.actual(); + if (theHttpStatus != RepHdr::sc407_ProxyAuthRequired || theError) { + theAuthXact->lifeTime = TheClock - theAuthXact->startTime; + theAuthXact->final = true; + } + } + + if (theOid.aborted()) + theActualRepType = TheUnknownContentId; + + CltXact::finish(err); +} + +// XXX: the needMore value and the return value are unused +bool HttpCltXact::controlledPostRead(bool &needMore) { + if (theState == stHdrWaiting) { + const Error err = getHeader(); + if (err || (theOid.connect() && theState == stBodyWaiting)) { + finish(err); + return false; + } + } + + if (theState == stBodyWaiting) { + getBody(); + return false; + } + + return false; +} + +Error HttpCltXact::getHeader() { + Assert(theState == stHdrWaiting); + + if (theRepHdr.parse(theConn->theRdBuf.content(), theConn->theRdBuf.contSize())) { + Error err = interpretHeader(); + + if (theRepHdr.theStatus != RepHdr::sc100_Continue) { + // dump reply header after interpretHeader() to dump more oid flags + static int respCount = 0; + if (!respCount++ || TheOpts.theDumpFlags(dumpRep, dumpHdr)) + printMsg(theConn->theRdBuf, theRepHdr.theHdrSize); + + if (!err) + err = handleAuth(); + if (!err) { + consume(theRepHdr.theHdrSize); + if (theHttpStatus == RepHdr::sc407_ProxyAuthRequired) + saveRepHeader(); + newState(stBodyWaiting); + } + } else { + if (!err) { + consume(theRepHdr.theHdrSize); + Assert(the100ContinueState == csWaiting); + the100ContinueState = csDone; + theMgr->resumeWriting(this); + } + theRepHdr.reset(); + theRepSize.reset(); + Assert(theState == stHdrWaiting); + } + + return err; + } else + if (expectMore()) { + if (theConn->theRdBuf.full()) + return errHugeHdr; + else + return 0; + } else { + const bool readNothing = !theConn->theRdBuf.contSize(); + // HTTP pconn race condition? + const bool race = readNothing && theConn->useCnt() > 1; + doRetry = doRetry || race; + if (race) + return errOther; + else + return readNothing ? errNoHdrClose : errPrematureEoh; + } + Should(false); // not reached + return errOther; +} + +void HttpCltXact::getBody() { + Assert(theState == stBodyWaiting); + Assert(theBodyParser); + + parse(); + + if (expectMore()) { + checkOverflow(); + return; + } + + const Size leftoverSize = unconsumed(); + + // no more data, set expected size if it was unknown + if (!theRepSize.expected().known()) + theRepSize.expect(theRepSize.actual() + leftoverSize); + + const bool pgAborted = cfgAbortedReply(); + + // make sure we do not leave anything behind and complain if needed + if (leftoverSize > 0) { + if (pgAborted) { + // no need to complain, a Polygraph-initiated abort + } else + if (theRepSize.actual() + leftoverSize < theRepSize.expected()) { + // no need for parser to complain since it is not a content error + } else { + const ParseBuffer leftovers(theConn->theRdBuf.content(), + leftoverSize); + theBodyParser->noteLeftovers(leftovers); + } + consume(leftoverSize); + } + + if (theRepSize.expected() == theRepSize.actual()) { + finish(0); + return; + } + + if (theRepSize.expected() < theRepSize.actual()) { + finish(errExtraRepData); + return; + } + + Assert(theRepSize.expected() > theRepSize.actual()); + + if (pgAborted) { + theConn->lastUse(true); + theOid.aborted(true); + finish(0); // not an error, configuration told server to abort + return; + } + + finish(errPrematureEof); +} + +bool HttpCltXact::controlledFill(bool &needMore) { + if (!CltXact::controlledFill(needMore)) + return false; + + if (the100ContinueState != csWaiting && theReqSize.expectToGetMore()) { + WrBuf &buf = theConn->theWrBuf; + // append request body if needed + const char *bodyStart = buf.space(); + if (theBodyIter && !theBodyIter->pour()) + return false; + theReqSize.got(buf.space() - bodyStart); + if (TheOpts.theDumpFlags(dumpReq, dumpBody)) + printMsg(bodyStart, buf.space() - bodyStart); + } + + return true; +} + +// possibly many transactions move on, after a single raw write +bool HttpCltXact::controlledPostWrite(Size &size, bool &needMore) { + CltXact::controlledPostWrite(size, needMore); + + if (!needMore) { + Assert(the100ContinueState != csWaiting); + if (theOid.foreignUrl()) + TheEmbedStats.foreignUrlRequested++; + } else + if (the100ContinueState == csWaiting) { + Assert(theState == stSpaceWaiting); + // We do not need to write more if we wrote the headers. + // HttpCltXact::controlledFill does not fill body in csWaiting. + if (theConn->theWrBuf.empty()) { // wrote all headers, now need to wait + needMore = false; // but will resumeWriting() + newState(stHdrWaiting); + } + } + + return true; +} + +void HttpCltXact::makeReq(WrBuf &buf) { + ofixedstream os(buf.space(), buf.spaceSize()); + + if (theState == stConnWaiting) { + if (theOid.connect()) + makeConnectReq(os); + else + makeExplicitReq(os); + newState(stSpaceWaiting); + } +} + +// make a CONNECT request +void HttpCltXact::makeConnectReq(ostream &os) { + os << rlpConnect; + Oid2UrlHost(theOid, true, os); + makeReqVersion(os); + makeHopByHopHdrs(os); + + static int reqCount = 0; + finishReqHdrs(os, !reqCount++); + + // no body for CONNECT requests + theReqOid.type(TheBodilessContentId); +} + +// make a non-CONNECT request +void HttpCltXact::makeExplicitReq(ostream &os) { + Assert(the100ContinueState == csNone); + Assert(!theReqContentCfg); + Assert(!theBodyIter); + // decide whether the request should have a body + if (theOid.post() || theOid.put()) { + theReqSize.expectedBody(true); + theReqContentCfg = theOwner->selectReqContent(theOid, theReqOid); + theBodyIter = theReqContentCfg->getBodyIter(theReqOid); + theBodyIter->start(&theConn->theWrBuf); + theReqBodySize = theBodyIter->contentSize(); + + static RndGen rng; + if (theReqBodySize > 0 && + ((theOwner->cfg()->theReqBodyPauseProb >= 0 && + rng.event(theOwner->cfg()->theReqBodyPauseProb)) || + (theOwner->cfg()->theReqBodyPauseStart >= 0 && + theReqBodySize >= theOwner->cfg()->theReqBodyPauseStart.byte()))) + the100ContinueState = csWaiting; + } else + theReqOid.type(TheBodilessContentId); + + // make headers + { + makeReqMethod(os); + makeEndToEndHdrs(os); + makeHopByHopHdrs(os); + makeCookies(os); // last, to know buffer space left + static int reqCount = 0; + finishReqHdrs(os, !reqCount++); + } + + // where we should abort + theAbortSize = theAbortCoord.pos(theReqSize.header(), theReqBodySize); +} + +void HttpCltXact::finishReqHdrs(ostream &os, bool forceDump) { + IOBuf &buf = theConn->theWrBuf; + const char *reqStart = buf.space(); + buf.appended((streamoff)os.tellp()); + + // give filters a chance, they may add their own headers + TheCltDataFilterRegistry().apply(this, buf); + + buf.append("\r\n", Min(buf.spaceSize(), Size(2))); // end-of-header + + Size fullSize = buf.space() - reqStart; + theReqSize.header(fullSize); + if (theReqBodySize.known()) + fullSize += theReqBodySize; + theReqSize.expect(fullSize); + theReqSize.got(buf.space() - reqStart); + + // dump request header + if (forceDump || TheOpts.theDumpFlags(dumpReq, dumpHdr)) + printMsg(reqStart, buf.space() - reqStart); +} + +void HttpCltXact::makeReqMethod(ostream &os) { + if (theOid.get()) + os << rlpGet; + else + if (theOid.post()) + os << rlpPost; + else + if (theOid.head()) + os << rlpHead; + else + if (theOid.put()) + os << rlpPut; + else + Assert(false); +} + +void HttpCltXact::makeReqVersion(ostream &os) { + if (theOwner->httpVersion() <= HttpVersion(1,0)) + os << rlsHttp1p0; + else + os << rlsHttp1p1; +} + +void HttpCltXact::makeEndToEndHdrs(ostream &os) { + + // send full URL only if we are talking directly to a proxy + if (theOwner->proxy(theOid) && !theConn->tunneling()) + Oid2Url(theOid, os); + else + Oid2UrlPath(theOid, os); + + makeReqVersion(os); + + /* request-header fields */ + + os << hfAccept; + if (const String *codings = theOwner->cfg()->theAcceptedContentCodings) + os << hfpAcceptEncoding << *codings << crlf; + + os << hfpHost; + Oid2UrlHost(theOid, false, os); + os << crlf; + + if (theOid.imsAny() && olcTimes().lmt() >= 0) { + const Time t = theOid.ims200() ? + (olcTimes().lmt() - Time::Sec(1)) : + (olcTimes().lmt() + Time::Sec(1)); + HttpDatePrint(os << hfpIMS, t) << crlf; + theReqFlags |= xfValidation; + } + + if (theOid.reload()) + os << hfReload; + + if (theOid.range()) { + RangeCfg::RangesInfo res = theOwner->makeRangeSet(os, theOid, *theRepContentCfg); + theRangeCount = res.theCount; + theRangesSize = res.theTotalSize; + } + + /* entity-header fields */ + + os << hfpXXact << TheGroupId << ' ' << theId << ' ' << hex << theReqFlags << dec << crlf; + + // send public world info + if (!theOid.foreignUrl()) { + if (const HostCfg *host = TheHostMap->at(theOid.viserv())) { + const PubWorld &pubWorld = *host->thePubWorld; + os << hfpXLocWorld << pubWorld.localSlice() << crlf; + + if (const PubWorldSlice *slice = pubWorld.sliceToSync()) + os << hfpXRemWorld << *slice << crlf; + } + + if (theSrvRep) + os << hfpXTarget << theSrvRep->addr() << crlf; + } + + os << hfpXAbort << theAbortCoord.whether() + << ' ' << theAbortCoord.where() << crlf; + + // report our readiness to change phase + os << hfpXPhaseSyncPos << TheStatPhaseMgr.phaseSyncPos() << crlf; + + if (theReqContentCfg && + theReqContentCfg->calcChecksumNeed(theReqOid)) + putChecksum(*theReqContentCfg, theReqOid, os); + + if (theBodyIter) { + theBodyIter->putHeaders(os); + const String &pfx(theReqContentCfg->url_pfx(theReqOid.hash())); + const String &ext(theReqContentCfg->url_ext(theReqOid.hash())); + if (pfx || ext) + os + << hfpContDisposition << '"' + << pfx << theReqOid.name() << ext + << '"' << crlf; + } + + Assert(the100ContinueState != csDone); + if (the100ContinueState == csWaiting) + os << hfExpect100Continue; + + makeOriginAuthHdr(os); +} + +void HttpCltXact::makeHopByHopHdrs(ostream &os) { + /* general-header fields */ + // persistency indication depends on HTTP version + if (theOwner->httpVersion() <= HttpVersion(1,0)) { + if (theConn->reusable()) + os << (theOwner->proxy(theOid) ? hfConnAlivePxy : hfConnAliveOrg); + } else { + if (!theConn->reusable()) + os << (theOwner->proxy(theOid) ? hfConnClosePxy : hfConnCloseOrg); + } + + makeProxyAuthHdr(os); +} + +void HttpCltXact::makeCookies(ostream &os) { + HttpCookies *const cookies(theOwner->cookies(theOid)); + + if (theOwner->doCookies()) { + // configure future response parser to collect new cookies + theRepHdr.collectCookies(cookies); + } + + // send back all cookies we kept (if any) + if (!cookies) + return; // but we may collect some from the response + + Assert(theOwner->doCookies()); + + for (const HttpCookie *cookie = cookies->first(); + cookie; + cookie = cookies->next()) { + + WrBuf &buf = theConn->theWrBuf; + const Size usedSize = (streamoff)os.tellp(); + const Size headerSize = + hfpCookie.len() + cookie->data().len() + 4; + const Size spaceRemaining = buf.spaceSize() - usedSize; + + if (headerSize < spaceRemaining) + os << hfpCookie << cookie->data() << crlf; + else + if (ReportError(errCookiesDontFit)) { + Comment << "header size: " << headerSize + << "; space left: " << spaceRemaining << endc; + } + } +} + +void HttpCltXact::makeProxyAuthHdr(ostream &os) { + const HttpAuthScheme scheme(theOwner->proxyAuthScheme(theOid)); + Connection::NtlmAuth &ntlmState(theConn->theProxyNtlmState); + makeAuthHdr(hfpProxyAuthorization, scheme, ntlmState, os); +} + +void HttpCltXact::makeOriginAuthHdr(ostream &os) { + const HttpAuthScheme scheme(theOwner->originAuthScheme(theOid)); + Connection::NtlmAuth &ntlmState(theConn->theOriginNtlmState); + makeAuthHdr(hfpAuthorization, scheme, ntlmState, os); +} + +void HttpCltXact::makeAuthHdr(const String &header, const HttpAuthScheme scheme, Connection::NtlmAuth &ntlmState, ostream &os) { + theOid.authCred(false); + switch (ntlmState.state) { + case ntlmNone: { // Initiating auth + if (scheme == authNtlm) { + makeAuthorization(header, scheme, os); + NtlmAuthPrintT1(os); + os << crlf; + ntlmState.state = ntlmSentT1; + } else + if (scheme == authNegotiate) { + makeAuthorization(header, scheme, os); + NegoNtlmAuthPrintT1(os, ntlmState.useSpnegoNtlm); + os << crlf; + ntlmState.state = ntlmSentT1; + } else + if (scheme == authBasic) { + if (theOwner->credentialsFor(theOid, theCred)) { + makeAuthorization(header, scheme, os); + PrintBase64(os, theCred.image().data(), theCred.image().len()); + os << crlf; + } + } else { + Should(scheme == authNone); + // no header, we have not been asked to authenticate + } + break; + } + case ntlmSentT1: { + if (theOwner->credentialsFor(theOid, theCred)) { + Area aUser = theCred.name(); + String sUser(aUser.data(), aUser.size()); + Area aPass = theCred.password(); + String sPass(aPass.data(), aPass.size()); + makeAuthorization(header, scheme, os); + if (NegoNtlmAuthPrintT3(os, + ntlmState.hdrRcvdT2.cstr(), + sUser.cstr(), + sPass.cstr(), + ntlmState.useSpnegoNtlm)) { + os << crlf; + ntlmState.state = ntlmSentT3; + } else { + os << crlf; + ntlmState.state = ntlmError; + } + } else { + ntlmState.state = ntlmError; + } + break; + } + case ntlmDone: + case ntlmError: { + // do nothing + break; + } + default: { + Should(false); + ntlmState.state = ntlmError; + } + } +} + +void HttpCltXact::makeAuthorization(const String &header, const HttpAuthScheme scheme, ostream &os) { + os << header; + switch (scheme) { + case authNtlm: + os << "NTLM "; + break; + case authNegotiate: + os << "Negotiate "; + break; + default: + os << "Basic "; + } +} + +Error HttpCltXact::interpretHeader() { + theOwner->absorbCookies(theOid, theRepHdr.theCookies); + theRepSize.header(theRepHdr.theHdrSize); + + // does reply line make sense? + if (!theRepHdr.theHttpVersion.known() || theRepHdr.theStatus < 0) + return errHttpRLine; + + if (100 <= theRepHdr.theStatus && theRepHdr.theStatus < 200) { + if (theRepHdr.theStatus == RepHdr::sc100_Continue) { + theContinueMsgTime = TheClock - theStartTime; + return the100ContinueState == csWaiting ? 0 : errUnexpected100Continue; + } else + return errUnsupportedControlMsg; + } + + if (theOid.connect() && theRepHdr.theStatus == RepHdr::sc200_OK) { + theRepSize.expect(theRepHdr.theHdrSize); + theActualRepType = TheBodilessContentId; + return 0; + } + + if (const Error err = setStatusCode(theRepHdr.theStatus)) + return err; + + if (theSrvRep) // TODO: why is this done here and not earlier or later? + theSrvRep->noteRequest(); + + // check content-length, set RepSize if possible + if (theRepHdr.expectBody()) { + if (theOid.head()) { + theActualRepType = TheBodilessContentId; + theRepSize.expect(theRepHdr.theHdrSize); + } else { + if (theRepHdr.theStatus == RepHdr::sc200_OK) + theActualRepType = theOid.type(); + else + theActualRepType = TheUnknownContentId; + theRepSize.expectedBody(true); + if (theRepHdr.theTransferEncoding == MsgHdr::tcChunked) { + if (theRepHdr.theContSize >= 0) // MUST ignore + ReportError(errChunkedButCLen); + } else + if (theRepHdr.theContSize >= 0) + theRepSize.expect(theRepHdr.theHdrSize + theRepHdr.theContSize); + else + if (theRepHdr.persistentConnection() && + !theRepHdr.multiRange()) + return errPersistButNoCLen; + } + } else { + theActualRepType = TheBodilessContentId; + if (theRepHdr.theContSize >= 0) + return errUnexpectedCLen; + theRepSize.expect(theRepHdr.theHdrSize); + } + + if (theRepHdr.theStatus == RepHdr::sc206_PartialContent) { + if (theRepHdr.theContRangeFirstByte >= 0 && + theRepHdr.theContRangeLastByte >= 0) { + if (theRepHdr.theContSize + theRepHdr.theContRangeFirstByte - theRepHdr.theContRangeLastByte != 1) + return errPartContBadByteRange; + if (theRepHdr.theContRangeInstanceLength >= 0 && + theRepHdr.theContRangeInstanceLength < theRepHdr.theContSize) + return errPartContBadInstanceLen; + } + } else + if (theRepHdr.theStatus != RepHdr::sc416_RequestedRangeNotSatisfiable) { + if (theRepHdr.theContRangeFirstByte >= 0 || + theRepHdr.theContRangeLastByte >= 0 || + theRepHdr.theContRangeInstanceLength >= 0) + return errUnexpectedCRange; + } + + // check that we can handle transfer encoding + if (theRepHdr.theTransferEncoding == MsgHdr::tcOther) + return errUnknownTransferEncoding; + + theOid.repToRedir(theRepHdr.redirect()); + + if (!theOid.connect() && + !theRepHdr.polyHeaders() && + theRepHdr.expectPolyHeaders()) { + if (const HostCfg *host = TheHostMap->at(theOid.viserv())) { + // do not expect Polygraph-specific headers from non-HTTP origins + if (host->theProtocol == Agent::pHTTP) { + theOid.foreignSrc(true); + noteError(errForeignSrc); + } + } + } + + checkAcl(); + + // firstHand here means our request reached the server and + // server's reply reached us + const bool firstHand = id().myMutant(theRepHdr.theXactId); + + theOid.cachable(theRepHdr.isCachable); + theOid.hit(!firstHand && theRepHdr.theXactId && theOid.basic()); + + if (theOid.hit()) { + if (!theOid.offeredHit() && !TheCltOpts.ignoreFalseHits) + noteError(errFalseHit); + + if (!theOid.cachable()) + noteError(errUnchbHit); + } + + if (firstHand) { // native miss + checkDateSync(); + firstHandSync(); + } else + if (theRepHdr.theXactId) { // native second-hand response + if (theOid.reload()) + noteError(errReloadHit); + } else + if (theRepHdr.theDate >= 0 && theRepHdr.theDate < HttpDateAtMost(theStartTime)) { + // our response is aged: it was generated before we asked for it + if (theOid.reload()) + noteError(errReloadHit); + } + + if (theOid.offeredHit() && !theOid.hit()) + noteError(errFalseMiss); + + checkFreshness(); + + // note if the server will close the connection + if (!theRepHdr.persistentConnection()) { + theConn->lastUse(true); + theMgr->noteLastXaction(this); + } + + theBodyParser = selectBodyParser(); + theRepFlags = theRepHdr.theXactFlags; + + return 0; +} + +void HttpCltXact::noteError(const Error &err) { + if (err == errFalseMiss) { + if (TheCltOpts.printFalseMisses) { + Oid2Url(theOid, cout << "False-Miss: "); + cout << endl; + printMsg(theConn->theRdBuf, theRepHdr.theHdrSize); + } + return; // do not report false misses; they may not be errors + } + + if (err == errForeignSrc) { + if (theOid.foreignUrl()) + return; // response to a foreign URL may be foreign + + static bool informed = false; + if (!informed) { + Comment(5) << "fyi: received first foreign response to " << + "Polygraph-specific URL:" << endc; + printMsg(theConn->theRdBuf); + informed = true; + } + + if (theHttpStatus == RepHdr::sc200_OK && theOwner->cfg()->acl()) + return; // let checkAcl() handle this case + + if (TheOpts.acceptForeignMsgs) + return; // the user told us to accept all foreign responses + } + + if (!ReportError(err)) + return; + + /* supply additional information */ + + if (err == errStaleHit || err == errReloadHit) { + HttpDatePrint(cout << "\trep: ", theRepHdr.theDate) << endl; + HttpDatePrint(cout << "\tlmt: ", olcTimes().lmt()) << endl; + HttpDatePrint(cout << "\treq: ", theStartTime) << endl; + HttpDatePrint(cout << "\tnow: " ) << endl; + HttpDatePrint(cout << "\texp: ", olcTimes().exp()) << endl; + } + + if (TheOpts.theDumpFlags(dumpErr, dumpAny)) + printMsg(theConn->theRdBuf); +} + +void HttpCltXact::saveRepHeader() { + theConn->theRdBuf.copyContent(theSavedRepHeader, theRepHdr.theHdrSize); +} + +void HttpCltXact::firstHandSync() { + // update public world info + if (theRepHdr.theRemWorld) + updatePubWorld(theRepHdr.theRemWorld); + + if (theSrvRep) + theSrvRep->noteFirstHandResponse(); + + // sync phases + if (theRepHdr.thePhaseSyncPos >= 0 && theRepHdr.theGroupId) + TheStatPhaseSync.notePhaseSync(theRepHdr.theGroupId, theRepHdr.thePhaseSyncPos); +} + +Error HttpCltXact::doForbidden() { + Error err; + if (theOwner->hasCredentials()) { + if (theCred.image()) { // authed + if (theCred.valid()) + err = errForbiddenAfterAuth; + } else + if (theAuthXact) // authing + err = errForbiddenDuringAuth; + else + err = errForbiddenBeforeAuth; + } else + err = errForbiddenWoutCreds; + return err; +} + +Error HttpCltXact::doProxyAuth() { + const bool needed(theHttpStatus == RepHdr::sc407_ProxyAuthRequired); + Connection::NtlmAuth &ntlmState(theConn->theProxyNtlmState); + const Error err(doAuth(true, needed, ntlmState)); + if (err && ReportError(err)) { + Comment << "robot: " << theOwner->host() + << " credentials: " << theCred.image() + << " proxy: " << theOwner->proxy(theOid) << endc; + } + return err; +} + +Error HttpCltXact::doOriginAuth() { + Error err; + // do no origin authentication during proxy authentication + if (theHttpStatus != RepHdr::sc407_ProxyAuthRequired) { + const bool needed(theHttpStatus == RepHdr::sc401_Unauthorized); + Connection::NtlmAuth &ntlmState(theConn->theOriginNtlmState); + err = doAuth(false, needed, ntlmState); + if (err && ReportError(err)) { + Comment << "robot: " << theOwner->host() + << " credentials: " << theCred.image() + << " origin: " << Oid2UrlHost(theOid) << endc; + } + } + return err; +} + +// For fresh connection auth scheme is determined by proxy response headers. +// If NTLM or Negotiate auth is started for a connection it never changes later. +// If a proxy changes auth scheme, all compound auth xactions that have not yet +// finished authenticating will fail. Other xactions will not be affected. +Error HttpCltXact::doAuth(const bool proxyAuth, const bool needed, Connection::NtlmAuth &ntlmState) { + Error authError; + + if (needed) { // denied + AuthChallenge &challenge(proxyAuth ? + theRepHdr.theProxyAuthenticate : + theRepHdr.theOriginAuthenticate); + if (challenge.scheme == authNone) + return proxyAuth ? errProxyAuthHeaders : errOriginAuthHeaders; + if (!theOwner->hasCredentials()) + return proxyAuth ? errProxyAuthWoutCreds : errOriginAuthWoutCreds; + switch (ntlmState.state) { + case ntlmNone: { + if (challenge.scheme == authNtlm) { + // sent nothing, proxy should signal disconnect, + // NTLM authentication will resume once we reconnect + doRetry = true; + authError = startAuth(authNtlm); + } else + if (challenge.scheme == authNegotiate) { + ntlmState.useSpnegoNtlm = isSpnegoNtlm(challenge.params.cstr()); + if (ntlmState.useSpnegoNtlm && theOwner->cfg()->theSpnegoAuthRatio >= 0) { + static RndGen rng; + ntlmState.useSpnegoNtlm = rng.event(theOwner->cfg()->theSpnegoAuthRatio); + } + + // According to rfc4559 spnego-based negotiate auth is not supposed + // to be used by proxies (but can be passed transparently to servers), + // so we can't really say whether we need to retry here or what. Need + // to test on available clients and proxies. + doRetry = ntlmState.useSpnegoNtlm; + + // sent nothing, in case of NTLMSSP proxy should signal disconnect, + // Negotiate authentication will resume once we reconnect + authError = startAuth(authNegotiate); + } else + if (!theCred.image()) { + // sent nothing, Basic auth + doRetry = true; + authError = startAuth(authBasic); + } else + if (theCred.valid()) { // sent valid Basic auth + authError = proxyAuth ? errProxyAuthAfterAuth : errOriginAuthAfterAuth; + } + // else auth correctly denied due to invalid credentials + break; + } + case ntlmSentT1: { + ntlmState.hdrRcvdT2 = challenge.params; + doRetry = true; // continue processing in HopByHops + break; + } + case ntlmSentT3: { // auth failed + ntlmState.state = ntlmError; + if (!theCred.valid()) // sent invalid NTLM auth + break; + // fall through + } + default: { // errors and weird cases + authError = proxyAuth ? errProxyAuthAfterAuth : errOriginAuthAfterAuth; + } + } + } else { // allowed + switch (ntlmState.state) { + case ntlmNone: { + // succeeded at Basic Auth + if (theCred.image() && !theCred.valid()) { // sent invalid + authError = proxyAuth ? errProxyAuthAllowed : errOriginAuthAllowed; + } + break; + } + case ntlmSentT3: { // as expected + ntlmState.state = ntlmDone; + if (!theCred.valid()) + authError = proxyAuth ? errProxyAuthAllowed : errOriginAuthAllowed; + break; + } + case ntlmDone: { + // do nothing, everything is ok + break; + } + default: { // fall though for all weird cases + authError = proxyAuth ? errProxyAuthAllowed : errOriginAuthAllowed; + } + } + } + return authError; +} + +Error HttpCltXact::startAuth(const HttpAuthScheme scheme) { + if (theAuthXact) + return errAuthBug; + + theAuthXact = CompoundXactInfo::Create(); + theAuthXact->startTime = theStartTime; + if (theHttpStatus == RepHdr::sc401_Unauthorized) + theOwner->noteOriginAuthReq(this, scheme); + else + theOwner->noteProxyAuthReq(this, scheme); + return 0; +} + +Error HttpCltXact::handleAuth() { + if (theHttpStatus == RepHdr::sc403_Forbidden) + return doForbidden(); + + Error err = doProxyAuth(); + if (!err) + err = doOriginAuth(); + return err; +} + +void HttpCltXact::redirect() { + ObjId dest = theRepHdr.theLocn.oid; + dest.rediredReq(true); + dest.get(true); + + if (!dest.foreignUrl()) { + if (!validRelOid(dest)) { + ReportError(errRedirLocation); + return; + } + + if (theRepHdr.theLocn.host) { + if (const Error err = setViserv(theRepHdr.theLocn.host, dest)) { + noteError(err); + return; + } + } else { + dest.viserv(theOid.viserv()); + } + + theOwner->selectTarget(dest); + dest.repeat(true); // polysrv only redirects to seen URLs + } else { + dest.type(TheForeignContentId); + dest.repeat(false); // XXX: may be a repeat, we do not know + } + // XXX: hot() is unset + + theOwner->noteRedirect(this, dest); +} + +BodyParser *HttpCltXact::selectBodyParser() { + BodyParser *cparser = selectContentParser(); + + if (theRepHdr.chunkedEncoding()) + return ChunkedCodingParser::GetOne(this, cparser); + if (theRepHdr.multiRange()) + return MultiPartParser::GetOne(this, cparser, theRepHdr.theBoundary); + + return cparser; +} + +BodyParser *HttpCltXact::selectContentParser() { + // do not parse if we are not going to request embedded objects + if (theOwner->cfg()->theEmbedRecurRatio <= 0) + return AnyBodyParser::GetOne(this); + + // parse if content is markup + if (theRepHdr.markupContent()) + return selectMarkupBodyParser(); + + // if content is unknown, use URL extension (if any) to guess type + if (!theRepHdr.knownContentType() && OidImpliesMarkup(theOid, theRepContentCfg)) + return selectMarkupBodyParser(); + + // parse if domestic content may have embedded tags + if (!theOid.foreignUrl() && theRepContentCfg->hasEmbedCont()) + return selectMarkupBodyParser(); + + // do not parse otherwise + return AnyBodyParser::GetOne(this); +} + +BodyParser *HttpCltXact::selectMarkupBodyParser() { + if (!thePage) { + thePage = PageInfo::Create(); + thePage->start = theStartTime; + } + if (theOwner->cfg()->followAllUris(theRepHdr)) + return UriScriptBodyParser::GetOne(this, theOwner->cfg()); + return MarkupBodyParser::GetOne(this, theOwner->cfg()); +} + +const ObjTimes &HttpCltXact::olcTimes() const { + // not calculated or not configured, but can be + if (theOlcTimes.lmt() < 0 && theRepContentCfg) + theRepContentCfg->calcTimes(theOid, theOlcTimes); + return theOlcTimes; +} + +void HttpCltXact::checkAcl() { + const AclGroup &acl = theOwner->cfg()->acl(); + if (!acl) // no access controls configured + return; + + if (!acl.needsCheck(theOid.foreignUrl())) + return; + + if (RepHdr::PositiveStatusCode(theHttpStatus)) { + if (!theOid.foreignUrl() && theOid.foreignSrc()) + checkAclMatch(acl.rewrite(), "rewritten"); + else + checkAclMatch(acl.allow(), "allowed"); + return; + } + + if (theHttpStatus == RepHdr::sc407_ProxyAuthRequired) { + // do not check acls if we did not authenticate yet but can + if (!theCred.image() && theOwner->hasCredentials()) + return; + // do not check acls if we sent invalid credentials + if (theCred.image() && !theCred.valid()) + return; + } + + checkAclMatch(acl.deny(), "denied"); +} + +void HttpCltXact::checkAclMatch(const RegExGroup *matchedGrp, const char *action) { + RegExMatchee m; + buildAclMatchee(m); + static Array matches; + matches.reset(); + theOwner->cfg()->acl().match(m, matches); + + bool explainMatch = false; + + if (matches.count() == 0) + explainMatch = ReportError(errAclNoMatches); + else + if (matches.count() == 1 && matches.last() != matchedGrp) + explainMatch = ReportError(errAclWrongMatch); + else + if (matches.count() > 1) + explainMatch = ReportError(errAclManyMatches); + + if (explainMatch) + explainAclMatch(m, action, matches); +} + +void HttpCltXact::buildAclMatchee(RegExMatchee &m) const { + // form complete URL + static WrBuf buf; + buf.reset(); + + ofixedstream os(buf.space(), buf.spaceSize()-Size(1)); + m.urlHost = buf.space() + 0; // URL host alone + Oid2UrlHost(theOid, false, os); + Should(os << ends); + + m.urlPath = buf.space() + os.tellp(); // URL path alone + Oid2UrlPath(theOid, os); + Should(os << ends); + + m.url = buf.space() + os.tellp(); // complete URL + Oid2Url(theOid, os); + Should(os << ends); + + buf.appended(Size(os.tellp())); + buf.append("", 1); // terminate even if stream is full + + m.userName = theCred.image().cstr(); + m.memberships = &theOwner->memberships(); +} + +void HttpCltXact::explainAclMatch(const RegExMatchee &m, const char *action, const Array &ourMatches) const { + ostream &os = Comment(6) << "URL: " << m.url << endl; + if (theCred.valid()) + os << "\tuser: " << theCred.image() << endl; + os << "\tmembership maps: " << m.memberships->count() << endl; + os << "\tgroups: "; + for (int g = 0; g < m.memberships->count(); ++g) { + if (g) + os << "; "; + dumpMatchingGroupNames(os, m.memberships->item(g)); + if (g >= 10) { + os << "; ..."; + break; + } + } + os << endl; + os << "\twas probably " << action + << " but matches " << ourMatches.count() << " rule(s)"; + for (int i = 0; i < ourMatches.count(); ++i) { + os << (i == 0 ? ": " : ", "); + os << theOwner->cfg()->acl().ruleName(ourMatches[i]); + } + os << endc; +} + +void HttpCltXact::dumpMatchingGroupNames(ostream &os, const MembershipMap *map) const { + MembershipMap::GroupIterator i = map->groupIterator(theCred); + for (int count = 0; i && count <= 5; ++count, ++i) { + if (count) + os << ", "; + os << *i; + } +} + +void HttpCltXact::checkFreshness() { + // cannot check without the date header + if (theRepHdr.theDate < 0) + return; + + // skip in-transit modicications to avoid false errors due to racing + if (HttpDateAtMost(theStartTime) <= olcTimes().lmt()) + return; + + // response is stale if it was generated before modification time + if (theRepHdr.theDate < olcTimes().lmt()) + noteError(errStaleHit); +} + +bool HttpCltXact::cfgAbortedReply() const { + Assert(theRepSize.expected().known()); + Size newRepSize = theRepSize.expected(); + + if (!theRepHdr.theAbortCoord) + return false; + + const Size abSz = + theRepHdr.theAbortCoord.pos(theRepHdr.theHdrSize, theRepSize.expected()-theRepHdr.theHdrSize); + + if (abSz < 0) + return false; + + /* expecting abort, sooner or later */ + + newRepSize = abSz; + + // we may have leftovers if we were parsing aborted content + if (abSz <= theRepSize.actual() + unconsumed()) + return true; // reached abort level + + return false; +} + +Error HttpCltXact::setStatusCode(int aStatus) { + theHttpStatus = aStatus; + if (theHttpStatus != RepHdr::sc200_OK && + theHttpStatus != RepHdr::sc202_Accepted && + theHttpStatus != RepHdr::sc206_PartialContent && + theHttpStatus != RepHdr::sc304_NotModified && + theHttpStatus != RepHdr::sc401_Unauthorized && + theHttpStatus != RepHdr::sc403_Forbidden && + theHttpStatus != RepHdr::sc407_ProxyAuthRequired && + theHttpStatus != RepHdr::sc416_RequestedRangeNotSatisfiable && + theHttpStatus != RepHdr::sc417_ExpectationFailed && + !theRepHdr.redirect()) + return errHttpStatusCode; + return Error(); +} + +void HttpCltXact::checkDateSync() { + if (theRepHdr.theDate < 0) { + ReportError(errHttpNoDate); + return; + } + + const Time maxGap = Time::Sec(60); + + // cannot measure drift using replies that took too long + if (TheClock - theStartTime >= maxGap/2) + return; + + if (theRepHdr.theDate > TheClock.time() + maxGap || + theRepHdr.theDate < TheClock.time() - maxGap) { + if (ReportError(errSyncDate)) { + HttpDatePrint(Comment << "request generated: ", theStartTime) << endc; + HttpDatePrint(Comment << "response generated: ", theRepHdr.theDate) << endc; + HttpDatePrint(Comment << "response received: ", TheClock.time()) << endc; + const Time diff = theRepHdr.theDate - TheClock.time(); + Comment << "time difference: " << diff << endc; + } + return; + } +} + +// called by BodyParsers, must not finish() +void HttpCltXact::noteContent(const ParseBuffer &content) { + if (theRepHdr.theChecksum.set()) + theCheckAlg.update(content.data(), content.size()); +} + +// called by BodyParsers, must not finish() +Error HttpCltXact::noteEmbedded(ReqHdr &hdr) { + TheEmbedStats.urlSeen++; + + const Error err = hdr.theUri.oid.foreignUrl() ? + handleForeignEmbedOid(hdr) : handleEmbedOid(hdr); + + if (!err) { + // "fix" new oid record + hdr.theUri.oid.repeat(theOid.repeat()); + hdr.theUri.oid.hot(theOid.hot()); + hdr.theUri.oid.ims200(theOid.ims200()); + hdr.theUri.oid.ims304(theOid.ims304()); + hdr.theUri.oid.reload(theOid.reload()); + hdr.theUri.oid.get(true); + theOwner->selectScheme(hdr.theUri.oid); + theOwner->noteEmbedded(this, hdr.theUri.oid); + } + + return err; +} + +// called by BodyParsers, must not finish() +void HttpCltXact::noteTrailerHeader(const ParseBuffer &hdr) { + if (ReportError(errTrailerHeader)) + printMsg(hdr.data(), hdr.size()); +} + +// called by BodyParsers, must not finish() +void HttpCltXact::noteEndOfTrailer() { + Should(!theRepSize.expectingWhatParsed()); + theRepSize.expectingWhatParsed(true); +} + +// called by BodyParsers, must not finish() +Error HttpCltXact::noteReplyPart(const RepHdr &hdr) { + Error err; + ++theBodyPartCount; + theBodyPartsSize += hdr.theContRangeLastByte - hdr.theContRangeFirstByte + Size(1); + Assert(theRangesSize.known() && theRangeCount.known()); + if (theBodyPartsSize > theRangesSize || + theBodyPartCount > theRangeCount) + err = errPartContBadCountOrSize; + return err; +} + +Error HttpCltXact::handleEmbedOid(ReqHdr &hdr) { + Error err; + if (validRelOid(hdr.theUri.oid)) { + if (hdr.theUri.host) + err = setViserv(hdr.theUri.host, hdr.theUri.oid); + else + hdr.theUri.oid.viserv(theOid.viserv()); // relative URL + + if (!err) + theOwner->selectTarget(hdr.theUri.oid); + } else { + err = errBadEmbedUri; + } + + return err; +} + +Error HttpCltXact::handleForeignEmbedOid(ReqHdr &hdr) { + Error err; + //if (strncmp(hdr.theUri.pathBuf, "/cgi-bin/cntmgr.pl", 18) == 0) + // TheEmbedStats.scriptMgrUrlSeen++; + + if (!hdr.theUri.host) { + // must set host name for Client to know where to send requests + hdr.theUri.oid.viserv(theOid.viserv()); + char buf[4*1024]; + ofixedstream os(buf, sizeof(buf)-1); + os << "http://"; + Oid2UrlHost(theOid, false, os); + os << hdr.theUri.oid.foreignUrl() << ends; + buf[sizeof(buf)-1] = '\0'; + hdr.theUri.oid.foreignUrl(buf); + } + + //if (TheEmbedStats.scriptUrlSeen % 1000 == 0) { + // (clog << here << "tag url: ").write(hdr.theUri.pathBuf, hdr.theUri.pathLen); + // clog << endl; + //} + + // hdr.theUri.oid is already set + return err; +} + +Error HttpCltXact::setViserv(const NetAddr &name, ObjId &oid) const { + // XXX: merge with SrvXact::setViserv() ? + int viserv = -1; + if (!TheHostMap->find(name, viserv)) + return errForeignHostName; + + if (!theOid.foreignUrl() && + viserv != theOid.viserv()) + return errSrvRedirect; // disallow for now + + oid.viserv(viserv); + return Error(); +} + +bool HttpCltXact::askedPeer() const { + return thePeerState != peerUnknown; +} + +bool HttpCltXact::usePeer() const { + return thePeerState == peerSome; +} + +void HttpCltXact::usePeer(bool doUse) { + thePeerState = doUse ? peerSome : peerNone; +} + +int HttpCltXact::cookiesSent() const { + int n(0); + if (theOwner->doCookies()) + if (const HttpCookies *cookies = theOwner->cookies(theOid)) + n = cookies->count(); + return n; +} + +int HttpCltXact::cookiesRecv() const { + return theRepHdr.theCookieCount; +} diff -ruBEN polygraph-4.3.1/src/client/HttpCltXact.h polygraph-4.3.1-mm/src/client/HttpCltXact.h --- polygraph-4.3.1/src/client/HttpCltXact.h 2010-12-22 23:42:25.000000000 +0000 +++ polygraph-4.3.1-mm/src/client/HttpCltXact.h 2011-03-04 23:15:41.000000000 +0000 @@ -79,7 +79,7 @@ void makeCookies(ostream &os); void makeProxyAuthHdr(ostream &os); void makeOriginAuthHdr(ostream &os); - void makeAuthHdr(const String &header, const HttpAuthScheme scheme, Connection::NtlmAuth &ntlmState, ostream &os); + void makeAuthHdr(const bool proxyAuth, const String &header, const HttpAuthScheme scheme, Connection::NtlmAuth &ntlmState, ostream &os); void makeAuthorization(const String &header, const HttpAuthScheme scheme, ostream &os); void firstHandSync(); diff -ruBEN polygraph-4.3.1/src/client/KerberosAuth.cc polygraph-4.3.1-mm/src/client/KerberosAuth.cc --- polygraph-4.3.1/src/client/KerberosAuth.cc 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.3.1-mm/src/client/KerberosAuth.cc 2011-03-04 23:15:41.000000000 +0000 @@ -0,0 +1,438 @@ +/* +*/ + +#include "base/polygraph.h" +#include "runtime/LogComment.h" +#include "runtime/NtlmAuthState.h" +#include "xstd/InAddress.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "client/KerberosAuth.h" +#include "xstd/gadgets.h" + +#if defined(HAVE_KRB5) && defined(HAVE_GSSAPI) + +#include "client/GssKrbCtx.h" + +#ifdef HAVE_ET_COM_ERR_H +#include +#elif defined(HAVE_COM_ERR_H) +#include +#else +#define error_message(code) krb5_get_err_text(theKrbCtx, code) +#endif + +#ifndef gss_mech_spnego +static gss_OID_desc _gss_mech_spnego = +{6, (void *) "\x2b\x06\x01\x05\x05\x02"}; +gss_OID gss_mech_spnego = &_gss_mech_spnego; +#endif + +int GssKrbXact::pton(const char *p) { + if (*p == '[') { + struct in6_addr inp; + // + // XXX ugly memory copy to remove [ ] + // + char copy[64]; + strncpy(copy, p+1, 64); + strtok(copy, "]"); + return inet_pton(AF_INET6, copy, &inp); + } else { + struct in_addr inp; + return inet_pton(AF_INET, p, &inp); + } +} + +static GssKrbXact gssEnvironment; +static GssKrbXact::KrbEnv krbEnvironment; + +// +// Setup Kerberos context for each Kerberos configuration file +// +GssKrbXact::KrbEnv::KrbEnv() { + krbUserp = NULL; + krbPasswdp = NULL; + krbServerName = NULL; + krbCfgPath = NULL; + krbSPN = NULL; + clearKrbCc = false; + theKrbCtx = NULL; + theKrbClt = NULL ; + theKrbCc = NULL; + theKrbErrorCode = 0; + theKrbCtxStruct = NULL; +} + +GssKrbXact::KrbEnv::~KrbEnv() { + krbCleanup(); +} + +NtlmAuthState GssKrbXact::KrbEnv::krbSetup() +{ + struct krbCtxStruct *kc; + static krb5_get_init_creds_opt options; + krb5_creds theKrbMemoryCreds; + krb5_deltat theKrbDeltaTime = 0; + String theKrbMaxTinme; + String theKrbMemoryCache; + String theKrbHostName; + String theKrbTgt; + + if (!krbServerName || !krbUserp || !krbPasswdp ) + return kerberosError; + + memset(&theKrbCreds, 0, sizeof(theKrbCreds)); + + kc = theKrbCtxStruct; + while (kc) { + if (!kc->theKrbCfgPath.cmp(krbCfgPath)) { + setenv("KRB5_CONFIG", krbCfgPath.cstr(), 1); + theKrbCtx = kc->theKrbCtx; + break; + } + kc = kc->theNextKrbCtx; + } + if (!kc) { + kc = new krbCtxStruct; + kc->theKrbCfgPath = krbCfgPath; + + setenv("KRB5_CONFIG", krbCfgPath.cstr(), 1); + theKrbErrorCode = krb5_init_context(&kc->theKrbCtx); + if (krbCheckError("krb5_init_context()")) + return kerberosError; + + // Setup ticket options - only once required + if (!theKrbCtxStruct) { + // Get a ticket valid for 7 days if kdc allows ir + theKrbMaxTinme = "7d"; + krb5_get_init_creds_opt_init(&options); + theKrbErrorCode = krb5_string_to_deltat((char *)theKrbMaxTinme.cstr(), &theKrbDeltaTime); + if (krbCheckError("krb5_string_to_deltat()")) + return kerberosError; + krb5_get_init_creds_opt_set_tkt_life(&options,theKrbDeltaTime); + } + + kc->theNextKrbCtx = theKrbCtxStruct; + theKrbCtxStruct = kc; + theKrbCtx = kc->theKrbCtx; + } + + theKrbHostName = krbServerName(0,krbServerName.find(':')); + + theKrbMemoryCache = "MEMORY:" + krbCfgPath + "_" + krbUserp; + setenv("KRB5CCNAME", theKrbMemoryCache.cstr(), 1); + + theKrbErrorCode = krb5_cc_resolve(theKrbCtx, theKrbMemoryCache.cstr(), &theKrbCc); + if (krbCheckError("krb5_cc_resolve()")) + return krbCleanup(); +// +// Check if cache has already valid credentails to avoid delay through +// krb5_get_init_creds_password call +// + + memset(&theKrbMemoryCreds, 0, sizeof(theKrbMemoryCreds)); + + theKrbTgt = krbSPN; + if (theKrbTgt.len() <= 0) { + if (pton(theKrbHostName.cstr())) { + Comment << "Warning hostname " << theKrbHostName << " is in IPV4/IPV6 address form" << endc; + } + theKrbTgt = "HTTP/" + theKrbHostName; + } + theKrbErrorCode = krb5_parse_name(theKrbCtx, theKrbTgt.cstr(), &theKrbMemoryCreds.server); + if (krbCheckError("krb5_parse_name(theKrbTgt)")) + return krbCleanup(); + + theKrbErrorCode = krb5_parse_name(theKrbCtx, krbUserp.cstr(), &theKrbMemoryCreds.client); + if (krbCheckError("krb5_parse_name(userp)")) + return krbCleanup(); + + theKrbErrorCode = krb5_cc_retrieve_cred(theKrbCtx, theKrbCc, KRB5_TC_MATCH_SRV_NAMEONLY, + &theKrbMemoryCreds, &theKrbCreds); + krb5_free_cred_contents(theKrbCtx, &theKrbMemoryCreds); + +// +// get new credentials if not in cache or (nearly) expired +// + if (clearKrbCc || theKrbErrorCode || theKrbCreds.times.endtime - time(0) < 300 ) { + krb5_free_cred_contents(theKrbCtx, &theKrbCreds); + + theKrbErrorCode = krb5_parse_name(theKrbCtx, krbUserp.cstr(), &theKrbClt); + if (krbCheckError("krb5_parse_name(userp(2))")) + return krbCleanup(); + + theKrbErrorCode = krb5_get_init_creds_password(theKrbCtx, &theKrbCreds, theKrbClt, + (char *)krbPasswdp.cstr(), NULL, 0, 0, NULL, &options); + if (krbCheckError("krb5_get_init_creds_password(" + krbUserp + ")")) + return krbCleanup(); + + theKrbErrorCode = krb5_cc_initialize(theKrbCtx, theKrbCc, theKrbClt); + if (krbCheckError("krb5_cc_initialize()")) + return krbCleanup(); + + // store credentials in memory cache for later re-use + theKrbErrorCode = krb5_cc_store_cred(theKrbCtx, theKrbCc, &theKrbCreds); + if (krbCheckError("krb5_cc_store_cred()")) + return krbCleanup(); + + } + + krbCleanup(); + return kerberosDone; +} + +int GssKrbXact::KrbEnv::krbCheckError(String theKrbFunctionName) { + if (theKrbErrorCode) { +// Does not work - error_message is defined in com_err but can not be found +// Comment << function << "failed with error: " << error_message(theKrbErrorCode) << endc; + Comment << theKrbFunctionName << "failed with error: " << theKrbErrorCode << endc; + return (1); + } + return (0); +} + +NtlmAuthState GssKrbXact::KrbEnv::krbCleanup() +{ + if (theKrbCtx) { + if (theKrbClt) { + krb5_free_principal(theKrbCtx, theKrbClt); + theKrbClt = NULL; + } + if (theKrbCc) { + krb5_cc_close(theKrbCtx, theKrbCc); + theKrbCc = NULL; + } + krb5_free_cred_contents(theKrbCtx, &theKrbCreds); + } + return kerberosError; +} + +// +// Setup GSS context +// + +GssKrbXact::GssKrbXact() { + theGssContext = GSS_C_NO_CONTEXT; + gssServerName = GSS_C_NO_NAME; +} + +void GssKrbXact::gssCleanup() { + + if (theGssContext != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&gssMinorStatus, &theGssContext, NULL); + if(gssServerName != GSS_C_NO_NAME) + gss_release_name(&gssMinorStatus, &gssServerName); + + theGssContext = GSS_C_NO_CONTEXT; + gssServerName = GSS_C_NO_NAME; +} + +int GssKrbXact::checkGssError(String gssFunctionName) +{ + if (GSS_ERROR(gssMajorStatus)) { + OM_uint32 gssMaj, gssMin; + OM_uint32 gssMsgCtx = 0; + gss_buffer_desc gssStatus; + String gssErrorString = NULL; + char *gssMsg; + + gssMsgCtx = 0; + do { /* convert major status code (GSS-API error) to text */ + gssMaj = gss_display_status(&gssMin, gssMajorStatus, + GSS_C_GSS_CODE, GSS_C_NULL_OID, &gssMsgCtx, &gssStatus); + if (gssMaj == GSS_S_COMPLETE && gssStatus.length > 0) { + gssMsg=(char *)malloc(gssStatus.length+1); + memcpy(gssMsg,gssStatus.value,gssStatus.length); + gssMsg[gssStatus.length]='\0'; + gssErrorString = gssErrorString + gssMsg; + free(gssMsg); + } else { + gssMsgCtx = 0; + } + gss_release_buffer(&gssMin, &gssStatus); + } while (gssMsgCtx); + + gssErrorString = gssErrorString + ". "; + gssMsgCtx = 0; + do { /* convert minor status code (underlying routine error) to text */ + gssMaj = gss_display_status(&gssMin, gssMinorStatus, + GSS_C_MECH_CODE, GSS_C_NULL_OID, &gssMsgCtx, &gssStatus); + if (gssMaj == GSS_S_COMPLETE && gssStatus.length > 0) { + gssMsg=(char *)malloc(gssStatus.length+1); + memcpy(gssMsg,gssStatus.value,gssStatus.length); + gssMsg[gssStatus.length]='\0'; + gssErrorString = gssErrorString + gssMsg; + free(gssMsg); + } else { + gssMsgCtx = 0; + } + gss_release_buffer(&gssMin, &gssStatus); + } while (gssMsgCtx); + + Comment << gssFunctionName << " failed with error: " << gssErrorString << endc; + return (1); + } + return (0); +} + +// +// Do the real work +// + +NtlmAuthState GssKrbXact::NegoKerberosAuthPrint(ostream & os, String gssBase64Token) +{ + gss_buffer_desc gssInputToken; + gss_buffer_desc gssOutputToken; + + size_t size; + unsigned char gssBuf[8096]; /* enough, unless the authorisation data is very + long */ + gssInputToken.length = 0; + gssInputToken.value = NULL; + gssOutputToken.length = 0; + gssOutputToken.value = NULL; + + if (gssBase64Token) { + const char * header = gssBase64Token.cstr(); + + /* skip initial whitespaces */ + while(*header && isspace((unsigned char)*header)) + header++; + + if (!*header) + return kerberosError; + + size = DecodeBase64(header, strlen(header), (char*)gssBuf, sizeof(gssBuf)/sizeof(gssBuf[0])); + + gssInputToken.length = size; + memcpy(gssInputToken.value,gssBuf,size); + } + + gssEnvironment.gssMajorStatus = gss_init_sec_context(&gssEnvironment.gssMinorStatus, + GSS_C_NO_CREDENTIAL, &gssEnvironment.theGssContext, gssEnvironment.gssServerName, + gss_mech_spnego, + 0, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + &gssInputToken, NULL, &gssOutputToken, NULL, NULL); + + if (gssEnvironment.checkGssError("gss_init_sec_context(" + krbEnvironment.krbUserp + ")")) + return kerberosError; + + if (gssOutputToken.length > sizeof(gssBuf)) { + Comment << "Token length " << gssOutputToken.length << " > buffer length " << sizeof(gssBuf) < 0) { + gssService.value = malloc(krbSPN.len()); + memcpy(gssService.value,krbSPN.cstr(),krbSPN.len()); + gssService.length = krbSPN.len(); + + // Import GSS Service + gssEnvironment.gssMajorStatus = gss_import_name(&gssEnvironment.gssMinorStatus, + &gssService, (gss_OID) GSS_C_NULL_OID, &gssEnvironment.gssServerName); + } else { + theGssServiceName = "HTTP@" + theGssHostName; + gssService.value = malloc(theGssServiceName.len()); + memcpy(gssService.value,theGssServiceName.cstr(),theGssServiceName.len()); + gssService.length = theGssServiceName.len(); + + // Import GSS Service + gssEnvironment.gssMajorStatus = gss_import_name(&gssEnvironment.gssMinorStatus, + &gssService, gss_nt_service_name, &gssEnvironment.gssServerName); + } + + gss_release_buffer(&gssEnvironment.gssMinorStatus, &gssService); + + if (gssEnvironment.checkGssError("gss_import_name(" + theGssServiceName + ")")) { + gssEnvironment.gssCleanup(); + return kerberosError; + } + + return gssEnvironment.NegoKerberosAuthPrint(os, NULL); + +} + +// +// Process reply and create new response +// + +NtlmAuthState CltGssXact::NegoKerberosAuthPrintT3(ostream & os, String gssBase64Token) +{ + return gssEnvironment.NegoKerberosAuthPrint(os, gssBase64Token); +} + +#else // No Kerberos support + +NtlmAuthState CltGssXact::NegoKerberosAuthPrintT1(ostream & os, String userp, String passwdp, + String server, String path, bool clear) +{ + Assert(false); + return kerberosError; +} + +NtlmAuthState CltGssXact::NegoKerberosAuthPrintT3(ostream & os, String base64Token) +{ + Assert(false); + return kerberosError; +} +#endif diff -ruBEN polygraph-4.3.1/src/client/KerberosAuth.h polygraph-4.3.1-mm/src/client/KerberosAuth.h --- polygraph-4.3.1/src/client/KerberosAuth.h 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.3.1-mm/src/client/KerberosAuth.h 2011-03-04 23:15:41.000000000 +0000 @@ -0,0 +1,13 @@ +#ifndef __HTTP_KERBEROS_AUTH_H +#define __HTTP_KERBEROS_AUTH_H + +class CltGssXact { + public: + static NtlmAuthState NegoKerberosAuthPrintT1(ostream &os, String userp, String passwdp, + String server, String kerberosConfigPath, + bool clearKerberosCache, String kerberosSPN); + static NtlmAuthState NegoKerberosAuthPrintT3(ostream &os, String base64Token); + +}; + +#endif diff -ruBEN polygraph-4.3.1/src/client/Makefile.am polygraph-4.3.1-mm/src/client/Makefile.am --- polygraph-4.3.1/src/client/Makefile.am 2010-12-10 18:25:55.000000000 +0000 +++ polygraph-4.3.1-mm/src/client/Makefile.am 2011-03-04 23:15:41.000000000 +0000 @@ -93,6 +93,9 @@ SpnegoCodec.cc \ NtlmAuth.h \ NtlmAuth.cc \ + GssKrbCtx.h \ + KerberosAuth.h \ + KerberosAuth.cc \ RangeCfg.h \ RangeCfg.cc \ SingleRangeCfg.h \ diff -ruBEN polygraph-4.3.1/src/pgl/RobotSym.cc polygraph-4.3.1-mm/src/pgl/RobotSym.cc --- polygraph-4.3.1/src/pgl/RobotSym.cc 2010-12-10 18:25:55.000000000 +0000 +++ polygraph-4.3.1-mm/src/pgl/RobotSym.cc 2011-03-04 23:15:41.000000000 +0000 @@ -51,6 +51,11 @@ static String strSocksProxies = "socks_proxies"; static String strRecurrence = "recurrence"; static String strSpnegoAuthRatio = "spnego_auth_ratio"; +static String strKerberosAuth = "kerberos_auth"; +static String strKerberosClearCache = "kerberos_clear_cache"; +static String strKerberosConfigPath = "kerberos_config_path"; +static String strKerberosProxySPN = "kerberos_proxy_spn"; +static String strKerberosServerSPN = "kerberos_server_spn"; static String strReq_inter_arrival = "req_inter_arrival"; static String strReq_methods = "req_methods"; static String strContainerTags = "container_tags"; @@ -89,6 +94,11 @@ theRec->bAdd(strTime_distr, strReq_inter_arrival, 0); theRec->bAdd(NumSym::TheType, strRecurrence, 0); theRec->bAdd(NumSym::TheType, strSpnegoAuthRatio, 0); + theRec->bAdd(BoolSym::TheType, strKerberosAuth, 0); + theRec->bAdd(BoolSym::TheType, strKerberosClearCache, 0); + theRec->bAdd(StringSym::TheType, strKerberosConfigPath, 0); + theRec->bAdd(StringSym::TheType, strKerberosProxySPN, 0); + theRec->bAdd(StringSym::TheType, strKerberosServerSPN, 0); theRec->bAdd(NumSym::TheType, strEmbed_recur, 0); theRec->bAdd(strStringArr, strInterests, 0); theRec->bAdd(strStringArr, strReq_types, 0); @@ -179,6 +189,27 @@ bool RobotSym::spnegoRatio(double &ratio) const { return getDouble(strSpnegoAuthRatio, ratio); } + +bool RobotSym::kerberos(bool &set) const { + return getBool(strKerberosAuth, set); +} + +bool RobotSym::kerberosClearCache(bool &set) const { + return getBool(strKerberosClearCache, set); +} + +String RobotSym::kerberosConfigPath() const { + return getString(strKerberosConfigPath); +} + +String RobotSym::kerberosProxySPN() const { + return getString(strKerberosProxySPN); +} + +String RobotSym::kerberosServerSPN() const { + return getString(strKerberosServerSPN); +} + bool RobotSym::embedRecurRatio(double &ratio) const { return getDouble(strEmbed_recur, ratio); } diff -ruBEN polygraph-4.3.1/src/pgl/RobotSym.cc.orig polygraph-4.3.1-mm/src/pgl/RobotSym.cc.orig --- polygraph-4.3.1/src/pgl/RobotSym.cc.orig 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.3.1-mm/src/pgl/RobotSym.cc.orig 2010-12-10 18:25:55.000000000 +0000 @@ -0,0 +1,377 @@ + +/* Web Polygraph http://www.web-polygraph.org/ + * (C) 2003-2006 The Measurement Factory + * Licensed under the Apache License, Version 2.0 */ + +#include "pgl/pgl.h" + +#include "xstd/String.h" +#include "xstd/rndDistrs.h" +#include "xstd/TblDistr.h" +#include "base/RndPermut.h" +#include "pgl/PglBoolSym.h" +#include "pgl/PglIntSym.h" +#include "pgl/PglNumSym.h" +#include "pgl/PglRateSym.h" +#include "pgl/PglRec.h" +#include "pgl/PglStringSym.h" +#include "pgl/PglNetAddrSym.h" +#include "pgl/PglArraySym.h" +#include "pgl/PglSizeSym.h" +#include "pgl/AclSym.h" +#include "pgl/ClientBehaviorSym.h" +#include "pgl/ContentSym.h" +#include "pgl/DnsResolverSym.h" +#include "pgl/SessionSym.h" +#include "pgl/RangeSym.h" +#include "pgl/RobotSym.h" + + +const String RobotSym::TheType = "Robot"; + +static String strAcl = "acl"; +static String strAddrArr = "addr[]"; +static String strAuth_error = "auth_error"; +static String strCredentials = "credentials"; +static String strDns_resolver = "dns_resolver"; +static String strEmbed_recur = "embed_recur"; +static String strForeign_trace = "foreign_trace"; +static String strRaw_uri_thrower = "raw_uri_thrower"; +static String strIcp_port = "icp_port"; +static String strInterests = "interests"; +static String strMinimize_new_conn = "minimize_new_conn"; +static String strOpen_conn_lmt = "open_conn_lmt"; +static String strOrigins = "origins"; +static String strPeer_http = "peer_http"; +static String strPeer_icp = "peer_icp"; +static String strPrivate_cache_cap = "private_cache_cap"; +static String strProxies = "proxies"; +static String strFtpProxies = "ftp_proxies"; +static String strHttpProxies = "http_proxies"; +static String strSocksProxies = "socks_proxies"; +static String strRecurrence = "recurrence"; +static String strSpnegoAuthRatio = "spnego_auth_ratio"; +static String strReq_inter_arrival = "req_inter_arrival"; +static String strReq_methods = "req_methods"; +static String strContainerTags = "container_tags"; +static String strReq_rate = "req_rate"; +static String strReq_types = "req_types"; +static String strSession = "session"; +static String strStringArr = "string[]"; +static String strTime_distr = "time_distr"; +static String strFloat_distr = "float_distr"; +static String strSize_distr = "size_distr"; +static String strUnique_urls = "unique_urls"; +static String strWait_xact_lmt = "wait_xact_lmt"; +static String strPipeline_depth = "pipeline_depth"; +static String strCookies_keep_lmt = "cookies_keep_lmt"; +static String strAcceptContentEncodings = "accept_content_encodings"; +static String strRanges = "ranges"; +static String strRangeArr = "Range[]"; +static String strReqBodyPauseProb = "req_body_pause_prob"; +static String strReqBodyPauseStart = "req_body_pause_start"; +static String strReqBodyRecurrence = "req_body_recurrence"; +static String strContentArr = "Content[]"; +static String strPostContents = "post_contents"; +static String strPutContents = "put_contents"; +static String strUploadContents = "upload_contents"; +static String strPassiveFtp = "passive_ftp"; +static String strSocksProb = "socks_prob"; +static String strSocksChainingProb = "socks_chaining_prob"; + +RobotSym::RobotSym(const String &aType): AgentSym(aType) { + theRec->bAdd(strAddrArr, strProxies, 0); + theRec->bAdd(strAddrArr, strFtpProxies, 0); + theRec->bAdd(strAddrArr, strHttpProxies, 0); + theRec->bAdd(strAddrArr, strSocksProxies, 0); + theRec->bAdd(strAddrArr, strOrigins, 0); + theRec->bAdd(RateSym::TheType, strReq_rate, 0); + theRec->bAdd(strTime_distr, strReq_inter_arrival, 0); + theRec->bAdd(NumSym::TheType, strRecurrence, 0); + theRec->bAdd(NumSym::TheType, strSpnegoAuthRatio, 0); + theRec->bAdd(NumSym::TheType, strEmbed_recur, 0); + theRec->bAdd(strStringArr, strInterests, 0); + theRec->bAdd(strStringArr, strReq_types, 0); + theRec->bAdd(strStringArr, strReq_methods, 0); + theRec->bAdd(strStringArr, strContainerTags, 0); + theRec->bAdd(strStringArr, strAcceptContentEncodings, 0); + theRec->bAdd(IntSym::TheType, strPrivate_cache_cap, 0); + theRec->bAdd(BoolSym::TheType, strUnique_urls, 0); + theRec->bAdd(IntSym::TheType, strOpen_conn_lmt, 0); + theRec->bAdd(IntSym::TheType, strWait_xact_lmt, 0); + theRec->bAdd(NumSym::TheType, strMinimize_new_conn, 0); + theRec->bAdd(strStringArr, strCredentials, 0); + theRec->bAdd(NumSym::TheType, strAuth_error, 0); + theRec->bAdd(AclSym::TheType, strAcl, new AclSym); + theRec->bAdd(SessionSym::TheType, strSession, new SessionSym); + theRec->bAdd(IntSym::TheType, strIcp_port, 0); + theRec->bAdd(NetAddrSym::TheType, strPeer_http, 0); + theRec->bAdd(NetAddrSym::TheType, strPeer_icp, 0); + theRec->bAdd(DnsResolverSym::TheType, strDns_resolver, new DnsResolverSym); + theRec->bAdd(StringSym::TheType, strForeign_trace, 0); + theRec->bAdd(StringSym::TheType, strRaw_uri_thrower, 0); + theRec->bAdd(strFloat_distr, strPipeline_depth, 0); + theRec->bAdd(strFloat_distr, strCookies_keep_lmt, 0); + theRec->bAdd(strRangeArr, strRanges, 0); + theRec->bAdd(NumSym::TheType, strReqBodyPauseProb, 0); + theRec->bAdd(SizeSym::TheType, strReqBodyPauseStart, 0); + theRec->bAdd(NumSym::TheType, strReqBodyRecurrence, 0); + theRec->bAdd(strContentArr, strPostContents, 0); + theRec->bAdd(strContentArr, strPutContents, 0); + theRec->bAdd(strContentArr, strUploadContents, 0); + theRec->bAdd(NumSym::TheType, strPassiveFtp, 0); + theRec->bAdd(NumSym::TheType, strSocksProb, 0); + theRec->bAdd(NumSym::TheType, strSocksChainingProb, 0); +} + +RobotSym::RobotSym(const String &aType, PglRec *aRec): AgentSym(aType, aRec) { +} + +bool RobotSym::isA(const String &type) const { + return type == TheType || AgentSym::isA(type); +} + +SynSym *RobotSym::dupe(const String &type) const { + if (type == ClientBehaviorSym::TheType) { + // cast Robot to ClientBehavior + ClientBehaviorSym *const cb = new ClientBehaviorSym(); + cb->rec()->copyCommon(*theRec); + return cb; + } + if (isA(type)) + return new RobotSym(this->type(), theRec->clone()); + return AgentSym::dupe(type); +} + +bool RobotSym::reqRate(double &rate) const { + return getRate(strReq_rate, rate); +} + +bool RobotSym::reqInterArrival(RndDistr *&iad) const { + iad = getDistr(strReq_inter_arrival); + double rr = -1; + const bool hasRR = reqRate(rr); + if (hasRR && iad) + cerr << loc() << ": cannot specify both request rate" + << " and inter-arrival distribution" << endl << xexit; + + if (iad) + return true; + + if (!hasRR) + return false; + + if (rr <= 0) + return true; // iad is nil + + // poisson request stream (default) is modeled using an exponential + // distribution with a mean of a given request rate + // all clients should share one rng + static RndGen rng(GlbPermut(rndRobotSymReqInterArrival)); + iad = new ExpDistr(&rng, 1/rr); + return true; +} + +bool RobotSym::recurRatio(double &ratio) const { + return getDouble(strRecurrence, ratio); +} + +bool RobotSym::spnegoRatio(double &ratio) const { + return getDouble(strSpnegoAuthRatio, ratio); +} +bool RobotSym::embedRecurRatio(double &ratio) const { + return getDouble(strEmbed_recur, ratio); +} + +bool RobotSym::uniqueUrls(bool &set) const { + return getBool(strUnique_urls, set); +} + +bool RobotSym::openConnLimit(int &lmt) const { + return getInt(strOpen_conn_lmt, lmt); +} + +bool RobotSym::waitXactLimit(int &lmt) const { + return getInt(strWait_xact_lmt, lmt); +} + +bool RobotSym::minimizeNewConn(double &prob) const { + return getDouble(strMinimize_new_conn, prob); +} + +SessionSym *RobotSym::session() const { + const SynSym *ss = getRecSym(strSession); + return ss ? + &((SessionSym&)ss->cast(SessionSym::TheType)) : 0; +} + +bool RobotSym::authError(double &prob) const { + return getDouble(strAuth_error, prob); +} + +bool RobotSym::icpPort(int &port) const { + return getInt(strIcp_port, port); +} + +NetAddr RobotSym::peerHttp() const { + return getNetAddr(strPeer_http); +} + +NetAddr RobotSym::peerIcp() const { + return getNetAddr(strPeer_icp); +} + +AclSym *RobotSym::acl() const { + SynSymTblItem *i = 0; + Assert(theRec->find(strAcl, i)); + return i->sym() ? + &((AclSym&)i->sym()->cast(AclSym::TheType)) : 0; +} + +DnsResolverSym *RobotSym::dnsResolver() const { + SynSymTblItem *di = 0; + Assert(theRec->find(strDns_resolver, di)); + return di->sym() ? + &((DnsResolverSym&)di->sym()->cast(DnsResolverSym::TheType)) : 0; +} + +bool RobotSym::privCache(int &capacity) const { + return getInt(strPrivate_cache_cap, capacity); +} + +bool RobotSym::proxies(Array &addrs) const { + return getNetAddrs(strProxies, addrs); +} + +bool RobotSym::ftpProxies(Array &addrs) const { + return getNetAddrs(strFtpProxies, addrs); +} + +bool RobotSym::httpProxies(Array &addrs) const { + return getNetAddrs(strHttpProxies, addrs); +} + +bool RobotSym::socksProxies(Array &addrs) const { + return getNetAddrs(strSocksProxies, addrs); +} + +bool RobotSym::origins(Array &addrs) const { + return getNetAddrs(strOrigins, addrs); +} + +bool RobotSym::origins(Array &addrs, RndDistr *&selector) const { + SynSymTblItem *oi = 0; + Assert(theRec->find(strOrigins, oi)); + if (!oi->sym()) + return false; // undefined + + // build address array and selector + origins(addrs); + ArraySym &os = (ArraySym&)oi->sym()->cast(ArraySym::TheType); + selector = os.makeSelector(kind() + "-origins"); + return true; +} + +String RobotSym::msgTypesField() const { + return strReq_types; +} + +bool RobotSym::credentials(Array &creds) const { + return getStrings(strCredentials, creds); +} + +bool RobotSym::containerTags(Array &tags) const { + return getStrings(strContainerTags, tags); +} + +bool RobotSym::acceptedContentCodings(Array &codings) const { + return getStrings(strAcceptContentEncodings, codings); +} + + +bool RobotSym::interests(Array &istrs, Array &iprobs) const { + if (ArraySym *a = getArraySym(strInterests)) { + a->copyProbs(iprobs); + ArraySymExportM(StringSym, *a, StringSym::TheType, istrs); + return true; + } + return false; +} + +RndDistr *RobotSym::interests(const TokenIdentifier &interestKinds) const { + return namesToDistr(strInterests, interestKinds); +} + +RndDistr *RobotSym::reqMethods(const TokenIdentifier &reqMethodNames) const { + return namesToDistr(strReq_methods, reqMethodNames); +} + +RndDistr *RobotSym::pipelineDepth() const { + return getDistr(strPipeline_depth); +} + +String RobotSym::foreignTrace() const { + return getString(strForeign_trace); +} + +String RobotSym::rawUriThrower() const { + return getString(strRaw_uri_thrower); +} + +RndDistr *RobotSym::cookiesKeepLimit() const { + return getDistr(strCookies_keep_lmt); +} + +bool RobotSym::ranges(Array &syms, RndDistr *&sel) const { + if (ArraySym *a = getArraySym(strRanges)) { + ArraySymExportM(RangeSym, *a, RangeSym::TheType, syms); + Array probs; + a->copyProbs(probs); + sel = TblDistr::FromDistrTable(type() + "-" + strRanges, probs); + return true; + } + return false; +} + +bool RobotSym::reqBodyPauseProb(double &f) const { + return getDouble(strReqBodyPauseProb, f); +} + +bool RobotSym::reqBodyPauseStart(BigSize &sz) const { + return getSize(strReqBodyPauseStart, sz); +} + +bool RobotSym::reqBodyRecurrence(double &f) const { + return getDouble(strReqBodyRecurrence, f); +} + +bool RobotSym::reqContents(const String ¶m, Array &syms, RndDistr *&sel) const { + if (ArraySym *a = getArraySym(param)) { + ArraySymExportM(ContentSym, *a, ContentSym::TheType, syms); + Array probs; + a->copyProbs(probs); + sel = TblDistr::FromDistrTable(type() + "-" + strPostContents, probs); + return true; + } + return false; +} + +bool RobotSym::passiveFtp(double &prob) const { + return getDouble(strPassiveFtp, prob); +} + +bool RobotSym::socksProb(double &prob) const { + return getDouble(strSocksProb, prob); +} + +bool RobotSym::socksChainingProb(double &prob) const { + return getDouble(strSocksChainingProb, prob); +} + +bool RobotSym::haveReqMethods() const { + return getArraySym(strReq_methods); +} + +bool RobotSym::haveReqTypes() const { + return getArraySym(strReq_types); +} diff -ruBEN polygraph-4.3.1/src/pgl/RobotSym.h polygraph-4.3.1-mm/src/pgl/RobotSym.h --- polygraph-4.3.1/src/pgl/RobotSym.h 2010-12-10 18:25:55.000000000 +0000 +++ polygraph-4.3.1-mm/src/pgl/RobotSym.h 2011-03-04 23:15:41.000000000 +0000 @@ -31,6 +31,11 @@ bool reqInterArrival(RndDistr *&iad) const; bool recurRatio(double &ratio) const; bool spnegoRatio(double &ratio) const; + bool kerberos(bool &set) const; + bool kerberosClearCache(bool &set) const; + String kerberosConfigPath() const; + String kerberosProxySPN() const; + String kerberosServerSPN() const; bool embedRecurRatio(double &ratio) const; bool uniqueUrls(bool &set) const; bool openConnLimit(int &lmt) const; diff -ruBEN polygraph-4.3.1/src/runtime/NtlmAuthState.h polygraph-4.3.1-mm/src/runtime/NtlmAuthState.h --- polygraph-4.3.1/src/runtime/NtlmAuthState.h 2007-05-12 01:04:16.000000000 +0100 +++ polygraph-4.3.1-mm/src/runtime/NtlmAuthState.h 2011-03-04 23:15:41.000000000 +0000 @@ -7,6 +7,6 @@ #define POLYGRAPH__RUNTIME_NTLM_AUTH_STATE_H typedef enum { ntlmNone, // No NTML is used or not initiated - ntlmSentT1, ntlmSentT3, ntlmDone, ntlmError } NtlmAuthState; + ntlmSentT1, ntlmSentT3, ntlmDone, ntlmError ,kerberosSentT1, kerberosDone, kerberosError} NtlmAuthState; #endif