// Warn about the dangers of exceeding String limits when manipulating HTTP
// headers. Technically, we do not concatenate _requests_, so we could relax
// their check, but we keep the two checks the same for simplicity sake.
- const auto safeRawHeaderValueSizeMax = (String::SizeMaxXXX()+1)/3;
+ const auto safeRawHeaderValueSizeMax = String::RawSizeMaxXXX();
// TODO: static_assert(safeRawHeaderValueSizeMax >= 64*1024); // no WARNINGs for default settings
if (Config.maxRequestHeaderSize > safeRawHeaderValueSizeMax)
debugs(3, DBG_CRITICAL, "WARNING: Increasing request_header_max_size beyond " << safeRawHeaderValueSizeMax <<
size_t bytes_used = 0;
wordlistDestroy(&ctrl.message);
-
- if (!parseControlReply(bytes_used)) {
- /* didn't get complete reply yet */
- scheduleReadControlReply(0);
+ try {
+ if (!parseControlReply(bytes_used)) {
+ /* didn't get complete reply yet */
+ scheduleReadControlReply(0);
+ return;
+ }
+ } catch (...) {
+ debugs(9, 2, "ERROR: Cannot parse control reply: " << CurrentException);
+ failed(ERR_FTP_FAILURE, 0);
return;
}
Ftp::Client::parseControlReply(size_t &bytesUsed)
{
char *s;
- char *sbuf;
char *end;
int usable;
int complete = 0;
wordlist *head = nullptr;
+ auto headDeleter = [](wordlist *h) { wordlistDestroy(&h); };
+ auto headGuard = std::unique_ptr<wordlist, decltype(headDeleter)>(head, headDeleter);
wordlist *list;
wordlist **tail = &head;
size_t linelen;
* We need a NULL-terminated buffer for scanning, ick
*/
const size_t len = ctrl.offset;
- sbuf = (char *)xmalloc(len + 1);
+ const auto sbufOwner = std::unique_ptr<void, decltype(&xfree)>(xmalloc(len + 1), xfree);
+ const auto sbuf = static_cast<char*>(sbufOwner.get());
xstrncpy(sbuf, ctrl.buf, len + 1);
end = sbuf + len - 1;
if (usable == 0) {
debugs(9, 3, "didn't find end of line");
- safe_free(sbuf);
return false;
}
s = sbuf;
s += strspn(s, crlf);
+ // cumulative length of parsed control reply lines added to the list
+ size_t replyLength = 0;
+
for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
if (complete)
break;
debugs(9, 5, "s = {" << s << "}");
linelen = strcspn(s, crlf) + 1;
+ replyLength += linelen;
if (linelen < 2)
break;
+ if (replyLength > String::RawSizeMaxXXX())
+ throw TextException(ToSBuf("control reply too long: ", replyLength, " exceeds safe limit of ", String::RawSizeMaxXXX(), " bytes"), Here());
+
if (linelen > 3)
complete = (*s >= '0' && *s <= '9' && *(s + 3) == ' ');
list = new wordlist();
+ if (!headGuard)
+ headGuard.reset(list);
list->key = (char *)xmalloc(linelen);
}
bytesUsed = static_cast<size_t>(s - sbuf);
- safe_free(sbuf);
if (!complete) {
- wordlistDestroy(&head);
return false;
}
- ctrl.message = head;
+ ctrl.message = headGuard.release();
assert(ctrl.replycode >= 0);
assert(ctrl.last_reply);
assert(ctrl.message);