From: Christos Tsantilas Date: Thu, 23 Jan 2014 18:10:31 +0000 (+0200) Subject: FEAT response with excessive whitespace X-Git-Tag: SQUID_3_5_0_1~117^2~19 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cb03c661889952a82565097fc8e585c5dd629892;p=thirdparty%2Fsquid.git FEAT response with excessive whitespace Some broken Microsoft FTP servers seem to be sending the following response to the FEAT command: 211-FEAT ....SIZE ....MDTM 211 END A single dot above represents a single space character, so instead of one space character they send 4 (four) space characters at the beginning of the internal FEAT lines. When this happens, the FTP client who sent the FEAT request to Squid gets nothing; its connection hangs. The problem is inside FtpHandleFeatReply function while parses the commands list. Arguments corresponding to an emty string passes to String::substr call (e->value.substr(beg-raw, end-raw) call) causing failure to a Must clause. This patch: - fixes and adds checks to avoid processing lines which does not include a command. - try to detect extra spaces before commands - try to add the same number of spaces before commands inserted by squid to FEAT commands list (EPSV and EPRT commands) --- diff --git a/src/client_side.cc b/src/client_side.cc index c4187e60d0..77b58f30a8 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -5339,18 +5339,27 @@ FtpHandleFeatReply(ClientSocketContext *context, const HttpReply *reply, StoreIO HttpHeaderPos pos = HttpHeaderInitPos; bool hasEPRT = false; bool hasEPSV = false; + int prependSpaces = 1; while (const HttpHeaderEntry *e = filteredHeader.getEntry(&pos)) { if (e->id == HDR_FTP_PRE) { // assume RFC 2389 FEAT response format, quoted by Squid: // <"> SP NAME [SP PARAMS] <"> + // but accommodate MS servers sending four SPs before NAME if (e->value.size() < 4) continue; const char *raw = e->value.termedBuf(); - if (raw[0] != '"' && raw[1] != ' ') + if (raw[0] != '"' || raw[1] != ' ') continue; - const char *beg = raw + 2; // after quote and space + const char *beg = raw + 1 + strspn(raw + 1, " "); // after quote and spaces // command name ends with (SP parameter) or quote const char *end = beg + strcspn(beg, " \""); + + if (end <= beg) + continue; + + // compute the number of spaces before the command + prependSpaces = beg - raw - 1; + const String cmd = e->value.substr(beg-raw, end-raw); if (!FtpSupportedCommand(cmd)) @@ -5363,13 +5372,16 @@ FtpHandleFeatReply(ClientSocketContext *context, const HttpReply *reply, StoreIO } } + char buf[256]; int insertedCount = 0; if (!hasEPRT) { - filteredHeader.putStr(HDR_FTP_PRE, "\" EPRT\""); + snprintf(buf, sizeof(buf), "\"%*s\"", prependSpaces + 4, "EPRT"); + filteredHeader.putStr(HDR_FTP_PRE, buf); ++insertedCount; } if (!hasEPSV) { - filteredHeader.putStr(HDR_FTP_PRE, "\" EPSV\""); + snprintf(buf, sizeof(buf), "\"%*s\"", prependSpaces + 4, "EPSV"); + filteredHeader.putStr(HDR_FTP_PRE, buf); ++insertedCount; }