_SQUID_INLINE_ int caseCmp(char const *, size_type count) const;
_SQUID_INLINE_ int caseCmp(String const &) const;
+ /// Whether creating a totalLen-character string is safe (i.e., unlikely to assert).
+ /// Optional extras can be used for overflow-safe length addition.
+ /// Implementation has to add 1 because many String allocation methods do.
+ static bool CanGrowTo(size_type totalLen, const size_type extras = 0) { return SafeAdd(totalLen, extras) && SafeAdd(totalLen, 1); }
+ /// whether appending growthLen characters is safe (i.e., unlikely to assert)
+ bool canGrowBy(const size_type growthLen) const { return CanGrowTo(size(), growthLen); }
+
String substr(size_type from, size_type to) const;
_SQUID_INLINE_ void cut(size_type newLength);
_SQUID_INLINE_ bool nilCmp(bool, bool, int &) const;
/* never reference these directly! */
- size_type size_; /* buffer size; 64K limit */
+ size_type size_; /* buffer size; limited by SizeMax_ */
size_type len_; /* current length */
+ static const size_type SizeMax_ = 65535; ///< 64K limit protects some fixed-size buffers
+ /// returns true after increasing the first argument by extra if the sum does not exceed SizeMax_
+ static bool SafeAdd(size_type &base, size_type extra) { if (extra <= SizeMax_ && base <= SizeMax_ - extra) { base += extra; return true; } return false; }
+
char *buf_;
_SQUID_INLINE_ void set(char const *loc, char const ch);
#include "squid.h"
#include "SquidString.h"
#include "StrList.h"
+#include "base/TextException.h"
/** appends an item to the list */
void
strListAdd(String * str, const char *item, char del)
{
assert(str && item);
+ const auto itemSize = strlen(item);
if (str->size()) {
char buf[3];
buf[0] = del;
buf[1] = ' ';
buf[2] = '\0';
+ Must(str->canGrowBy(2));
str->append(buf, 2);
}
- str->append(item, strlen(item));
+ Must(str->canGrowBy(itemSize));
+ str->append(item, itemSize);
}
/** returns true iff "m" is a member of the list */
String::setBuffer(char *aBuf, String::size_type aSize)
{
assert(undefined());
- assert(aSize < 65536);
+ assert(aSize <= SizeMax_);
buf_ = aBuf;
size_ = aSize;
}
} else {
// Create a temporary string and absorb it later.
String snew;
- assert(len_ + len < 65536); // otherwise snew.len_ overflows below
+ assert(canGrowBy(len)); // otherwise snew.len_ may overflow below
snew.len_ = len_ + len;
snew.allocBuffer(snew.len_ + 1);
startedAdaptation(false),
#endif
receivedWholeRequestBody(false),
+ doneWithFwd(nullptr),
theVirginReply(NULL),
theFinalReply(NULL)
{
HTTPMSGUNLOCK(theVirginReply);
HTTPMSGUNLOCK(theFinalReply);
- fwd = NULL; // refcounted
-
if (responseBodyBuffer != NULL) {
delete responseBodyBuffer;
responseBodyBuffer = NULL;
cleanAdaptation();
#endif
+ if (!doneWithServer())
+ closeServer();
+
+ if (!doneWithFwd) {
+ doneWithFwd = "swanSong()";
+ fwd->handleUnregisteredServerEnd();
+ }
+
BodyConsumer::swanSong();
#if USE_ADAPTATION
Initiator::swanSong();
{
debugs(11,5, HERE << "completing forwarding for " << fwd);
assert(fwd != NULL);
+ doneWithFwd = "completeForwarding()";
fwd->complete();
}
#endif
bool receivedWholeRequestBody; ///< handleRequestBodyProductionEnded called
+ /// whether we should not be talking to FwdState; XXX: clear fwd instead
+ /// points to a string literal which is used only for debugging
+ const char *doneWithFwd;
+
private:
void sendBodyIsTooLargeError();
void maybePurgeOthers();
{
debugs(9, 4, status());
ctrl.clear();
+ doneWithFwd = "ctrlClosed()"; // assume FwdState is monitoring too
mustStop("Ftp::Client::ctrlClosed");
}
scheduleReadControlReply(1);
}
-/**
- * Quickly abort the transaction
- *
- \todo destruction should be sufficient as the destructor should cleanup,
- * including canceling close handlers
- */
void
Ftp::Client::abortAll(const char *reason)
{
debugs(9, 3, "aborting transaction for " << reason <<
"; FD " << (ctrl.conn!=NULL?ctrl.conn->fd:-1) << ", Data FD " << (data.conn!=NULL?data.conn->fd:-1) << ", this " << this);
- if (Comm::IsConnOpen(ctrl.conn)) {
- ctrl.conn->close();
- return;
- }
-
- fwd->handleUnregisteredServerEnd();
- mustStop("Ftp::Client::abortTransaction");
+ mustStop(reason);
}
/**
HttpStateData::httpStateConnClosed(const CommCloseCbParams ¶ms)
{
debugs(11, 5, "httpStateFree: FD " << params.fd << ", httpState=" << params.data);
+ doneWithFwd = "httpStateConnClosed()"; // assume FwdState is monitoring too
mustStop("HttpStateData::httpStateConnClosed");
}
String strFwd = hdr_in->getList(Http::HdrType::X_FORWARDED_FOR);
- if (strFwd.size() > 65536/2) {
+ // if we cannot double strFwd size, then it grew past 50% of the limit
+ if (!strFwd.canGrowBy(strFwd.size())) {
// There is probably a forwarding loop with Via detection disabled.
// If we do nothing, String will assert on overflow soon.
// TODO: Terminate all transactions with huge XFF?
Client::sentRequestBody(io);
}
-// Quickly abort the transaction
-// TODO: destruction should be sufficient as the destructor should cleanup,
-// including canceling close handlers
void
HttpStateData::abortAll(const char *reason)
{
debugs(11,5, HERE << "aborting transaction for " << reason <<
"; " << serverConnection << ", this " << this);
-
- if (Comm::IsConnOpen(serverConnection)) {
- serverConnection->close();
- return;
- }
-
- fwd->handleUnregisteredServerEnd();
- mustStop("HttpStateData::abortAll");
+ mustStop(reason);
}