- Replace Handshake::details pointer with an always-available object
- Replace Security::ProtocolVersion and its "int" representation in TlsDetails
and NegotiationHistory classes with the existing Anyp::ProtocolVersion
- Fix TlsDetails::compressMethod. The clients may send a compression methods
list with a NULL compression method.
Rename to TlsDetails::compressionSupported.
- Other minor fixes.
Alex Rousskov [Tue, 3 May 2016 17:18:39 +0000 (11:18 -0600)]
Optimization: Spend less CPU and RAM on adjustSSL(). Speed gain: ~5%.
Do not store extension types just to iterate over them in adjustSSL().
Check for extension support while parsing instead. Since the list of
OpenSSL-supported extensions is constant (does not depend on the
connection), we do not need to create and index extension storage once
for each TLS connection; we now do it once per worker lifetime instead.
Use std::unordered_set instead of std::list for ciphers. Most real-world
cipher lists probably contain dozens of 2-byte entries, making std::list
storage a poor choice. Unlike TLS extensions, supported ciphers depend
on the connection so we have to store all of them to check whether each
stored cipher is supported for the SSL connection object created later.
Having an O(1) lookup speeds up that last check a lot compared to the
old linear search across all stored ciphers.
Do fast adjustSSL() checks before the longer cipher loop check.
Added TLSEXT_TYPE_signature_algorithms(13) and
TLSEXT_TYPE_next_proto_neg(13172) to the list of TLS extensions
supported by OpenSSL and recognized by Squid. Recognizing these
extensions is necessary for adjustSSL() to work in more real-world
cases.
Also sorted TLSEXT_TYPE_* entries and replaced "#if 0" code with a way
to build Squid to recognize more extensions as OpenSSL's list grows.
Alex Rousskov [Mon, 2 May 2016 16:38:05 +0000 (10:38 -0600)]
Finalized BinaryTokenizer context handling. Polished.
No more funny context fields inside TLS structures. Context is handled
by the parsing code without needlessly storing it long-term.
Hid TLS structures/parsers used exclusively by
Security::HandshakeParser inside security/Handshake.cc to simplify API.
Also skipped unused ServerHello.random (instead of storing it in
TlsDetails::clientRandom) and replaced SQUID_TLS_RANDOM_SIZE macro
with a regular C++ constant.
Alex Rousskov [Sat, 30 Apr 2016 03:38:26 +0000 (21:38 -0600)]
Stop parsing SSL records after a fatal SSL Alert.
The fatal alert sender should close the connection. Waiting for the next
record is pointless and will obscure the problem when we eventually read
the EOF on the socket.
Alex Rousskov [Sat, 30 Apr 2016 03:35:46 +0000 (21:35 -0600)]
Separated BinaryTokenizer commits from context debugging. Polished.
Commits are relatively rare events specific to incremental parsing. Most
parsers are not incremental and do not commit/rollback. However, all
parsers need to debug what they parse. Thus, it was wrong to combine
commits with context debugging.
BinaryTokenizer single-context debugging did not support nested contexts
(such as Hello.version.major) and reported wrong FieldGroup sizes for
some parsed structures. The new BinaryTokenizerContext does not have
these problems and is more general (but still needs more polishing
work).
Also polished many field names, comments, debug messages, and some code.
Earlier code required the caller to first create a PString object and
then extract its .data field to get to the vector contents as an SBuf.
However, that approach just increased code and logging noise because
that SBuf itself (including SBuf.length()) is all the caller may need!
Alex Rousskov [Fri, 29 Apr 2016 00:27:45 +0000 (18:27 -0600)]
Fixed parsing of server-sent SNI.
The old code could not handle an empty SNI extension that most servers
send. RFC 6066 prose instructs servers to send empty SNI extensions, and
the formal SNI grammar is apparently client-specific. We are not the
only ones being confused by that because there are severs that send
empty ServerNameLists, which are actually prohibited by the grammar.
Alex Rousskov [Sat, 23 Apr 2016 04:46:12 +0000 (22:46 -0600)]
Removed HandshakeParser::parseError and hid/renamed its parseDone.
The presence of two "persistent" parsing outcomes (done and error) was
confusing to the callers: Which one do I check and when? The adjusted
interface uses exceptions for errors and a false parseHandshake() return
value to signal "need more data". This simplifies the API and untangles
the calling code quite a bit.
Alex Rousskov [Fri, 22 Apr 2016 00:12:20 +0000 (18:12 -0600)]
Lack of data at EOF is a parsing error, not a "need more" condition.
Branch changes did not distinguish "need more and can expect more" from
"need more but got everything" situations. When one of the inner parsers
that deal with "complete" buffers failed, it would throw
InsufficientInput exception and the higher level parsing code would
interpret that as a sign that more SSL records/fragments are needed,
resulting in stuck transactions instead of parsing errors.
Unlike parsing errors, stuck transactions cannot be bypassed or ignored
so the difference is important.
Initially, they looked like we were [incorrectly] subtracting -1 from
some buffer length (read -1, pass 5; read -1, pass 4). Now, I believe
they [just] indicated unnecessary network reads. The fixed sequence
looks similar to this (note no network reads):
| bio.cc(289) giveBuffered: Pass 5 read bytes to openSSL
| bio.cc(289) giveBuffered: Pass 4 read bytes to openSSL
The refactored ServerBio code starts in "parsing" state (SSL Hello
parsing is the primary ServerBio functionality). Only when that parsing
is over, ServerBio starts feeding OpenSSL with received bytes. This
internal logic allows us to hide parsing from callers and avoid the
confusing public holdRead API.
Record SSL client and SSL server details (supported TLS version/requested
TLS version) for fast-sni branch
Currently the fast-SNI-peek branch does not parse SSL client hello in
Client-First and Server-First bumping modes. This patch always "peeks" the
SSL client hello message and apply the squid TLS parser for Client-First
and Server-First modes to.
Also this patch moves the code which retrieves SSL server details from
PeekingPeerConnector class to PeerConnector class t retrieve details for all
SSL server side connections.
- remove uneeded members from classes
- Remove the TlsDetails object from ClientBio
- Enable SSL Server Hello parsing code
- remove uneeded code
- Fix bump client first and bump server first bumping modes
Squid parses incomming client SSL hello mesage, before create any openSSL
related structures and objects. After acl check at bumping step2.
Actually creating openSSL objects for client side still can be delayed
untill the server side is finishes. The only reason to create openSSL
structures imediatelly after step2 is to use openSSL to check for unsupported
comunications features and settings and fallback to spliceOnError.
Regression:
Squid does not parses client Hello message in the case of bump-server-first
and bump-client-first.
The supported and requested SSL versions (i %ssl::>received_supported_version
and %ssl::>received_hello_version formating codes ) can not be logged for
these modes.
Improve TLS/SSL parsing code in Handshale.cc and use it inside bio.cc for client
and server messages
- full implementation for TLS and SSLv2 parsers inside Handshake.cc/h files
- remove parsing code from bio.cc
- Store parsed info in new Security::TlsDetails struct and remove the
Ssl::sslFeatures class
- improve SSLv2 parsing code.
Bug description:
- The client side and server side are finished
- On server side the Ftp::Relay::finalizeDataDownload() is called and
schedules the Ftp::Server::originDataCompletionCheckpoint
- On client side the "Ftp::Server::userDataCompletionCheckpoint" is
called. This is schedules a write to control connection and closes
data connection.
- The Ftp::Server::originDataCompletionCheckpoint is called which is
trying to write to control connection and the assertion triggered.
This bug is an corner case, where the client-side (FTP::Server) should
wait for the server side (Ftp::Client/Ftp::Relay) to finish its job before
respond to the FTP client. In this bug the existing mechanism, designed
to handle such problems, did not worked correctly and resulted to a double
write response to the client.
This patch try to fix the existing mechanism as follows:
- When Ftp::Server receives a "startWaitingForOrigin" callback, postpones
writting possible responses to the client and keeps waiting for the
stopWaitingForOrigin callback
- When the Ftp::Server receives a "stopWaitingForOrigin" callback,
resumes any postponed response.
- When the Ftp::Client starts working on a DATA-related transaction, calls the
Ftp::Server::startWaitingForOrigin callback
- When the Ftp::Client finishes its job or when its abort abnormaly, checks
whether it needs to call Ftp::Server::stopWaitingForOrigin callback.
- Also this patch try to fix the status code returned to the FTP client
taking in account the status code returned by FTP server. The
"Ftp::Server::stopWaitingForOrigin" is used to pass the returned status code
to the client side.
Author: Eduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
Added ACL-driven server_pconn_for_nonretriable squid.conf directive.
This directive provides fine-grained control over persistent connection
reuse when forwarding HTTP requests that Squid cannot retry. It is
useful in environments where opening new connections is very expensive
and race conditions associated with persistent connections are very rare
and/or only cause minor problems.
Alex Rousskov [Sat, 12 Mar 2016 18:40:29 +0000 (11:40 -0700)]
Trying to avoid "looser throw specifier" error with Wheezy GCC.
AFAICT, the default CbdataParent destructor gets implicit
"noexcept(true)" specifier (because the default destructor does not
throw itself, and CbdataParent has no data members or parents that could
have contributed potentially throwing destructors). The AsyncJob child
uses a lot of things that might throw during destruction (the compiler
cannot tell for sure because we do not use noexcept specifiers). Thus,
the compiler has to use "noexcept(false)" specifier for ~AsyncJob, which
is "looser" that "noexcept(true)" for ~CbdataParent and, hence, violates
the parent interface AsyncJob is implementing/overriding.
I have doubts about the above analysis because many other compilers,
including GCC v5 and clang are happy with the default virtual
CbdataParent destructor. If my analysis is correct, then the rule of
thumb is: Base classes must not use "= default" destructors until all
our implicit destructors become "noexcept".
AsyncJob classes can now use C++11 overrides as long as they use the new
CBDATA_CHILD() macro instead of old CBDATA_CLASS().
I have prohibited multiple CBDATA_CHILD() classes on the same
inheritance branch by adding the "final" specifier to toCbdata(). Such
classes feel dangerous because they may have different sizes and it is
not obvious to me whether the cbdata code will call the right size-
specific delete for them. We can easily relax this later if needed.
Alex Rousskov [Fri, 11 Mar 2016 18:00:51 +0000 (11:00 -0700)]
Bug 7: Update cached entries on 304 responses.
New Store API to update entry metadata and headers on 304s.
Support entry updates in shared memory cache and rock cache_dirs.
No changes to ufs-based cache_dirs: Their entries are still not updated.
* Atomic StoreEntry metadata updating
StoreEntry metadata (swap_file_sz, timestamps, etc.) is used
throughout Squid code. Metadata cannot be updated atomically because
it has many fields, but a partial update to those fields causes
assertions. Still, we must update metadata when updating HTTP
headers. Locking the entire entry for a rewrite does not work well
because concurrent requests will attempt to download a new entry
copy, defeating the very HTTP 304 optimization we want to support.
Ipc::StoreMap index now uses an extra level of indirection (the
StoreMap::fileNos index) which allows StoreMap control which
anchor/fileno is associated with a given StoreEntry key. The entry
updating code creates a disassociated (i.e., entry/key-less) anchor,
writes new metadata and headers using that new anchor, and then
_atomically_ switches the map to use that new anchor. This allows old
readers to continue reading using the stale anchor/fileno as if
nothing happened while a new reader gets the new anchor/fileno.
Shared memory usage increase: 8 additional bytes per cache entry: 4
for the extra level of indirection (StoreMapFileNos) plus 4 for
splicing fresh chain prefix with the stale chain suffix
(StoreMapAnchor::splicingPoint). However, if the updated headers are
larger than the stale ones, Squid will allocate shared memory pages
to accommodate for the increase, leading to shared memory
fragmentation/waste for small increases.
* Revamped rock index rebuild process
The index rebuild process had to be completely revamped because
splicing fresh and stale entry slot chain segments implies tolerating
multiple entry versions in a single chain and the old code was based
on the assumption that different slot versions are incompatible. We
were also uncomfortable with the old cavalier approach to accessing
two differently indexed layers of information (entry vs. slot) using
the same set of class fields, making it trivial to accidentally
access entry data while using slot index.
During the rewrite of the index rebuilding code, we also discovered a
way to significantly reduce RAM usage for the index build map (a
temporary object that is allocated in the beginning and freed at the
end of the index build process). The savings depend on the cache
size: A small cache saves about 30% (17 vs 24 bytes per entry/slot)
while a 1TB cache_dir with 32KB slots (which implies uneven
entry/slot indexes) saves more than 50% (~370MB vs. ~800MB).
Adjusted how invalid slots are counted. The code was sometimes
counting invalid entries and sometimes invalid entry slots. We should
always count _slots_ now because progress is measured in the number
of slots scanned, not entries loaded. This accounting change may
surprise users with much higher "Invalid entries" count in cache.log
upon startup, but at least the new reports are meaningful.
This rewrite does not attempt to solve all rock index build problems.
For example, the code still assumes that StoreEntry metadata fits a
single slot which is not always true for very small slots.
Alex Rousskov [Fri, 11 Mar 2016 17:24:13 +0000 (10:24 -0700)]
Removed SWAPOUT_WRITING assertion from storeSwapMetaBuild().
I do not see any strong dependency of that code on that state and we
need to be able to build swap metadata when updating a stale entry
(which would not normally be in the SWAPOUT_WRITING state).
The biggest danger is that somebody calls storeSwapMetaBuild() when the
entry metadata is not yet stable. I am not sure we have a way of
detecting that without using something as overly strong as
SWAPOUT_WRITING.
Squid crashes on shutdown while cleaning up idle ICAP connections.
The global Adaptation::Icap::TheConfig object is automatically
destroyed when Squid exits. Its destructor destroys Icap::ServiceRep
objects that, in turn, close all open connections in the idle
connections pool. Since this happens after comm_exit has destroyed all
Comm structures associated with those connections, Squid crases.
Amos Jeffries [Tue, 1 Mar 2016 02:57:50 +0000 (15:57 +1300)]
RFC 7725: Add registry entry for 451 status text
While Squid does not generate these messages automatically we still have
to relay the status line text accurately, and admin may want to use it
for deny_info status.
After certain failures, FwdState::retryOrBail() may be called twice,
once from FwdState::unregisterdServerEnd() [called from
HttpStateData::swanSong()] and once from the FwdState's own connection
close handler. This may result in two concurrent connections to the
remote server, followed by an assertion upon a connection closure.
This patch:
- After HttpStateData failures, instead of closing the squid-to-peer
connection directly (and, hence, triggering closure handlers), calls
HttpStateData::closeServer() and mustStop() for a cleaner exit with
fewer wasteful side effects and better debugging.
- Creates and remembers a FwdState close handler AsyncCall so that
comm_remove_close_handler() can cancel an already scheduled callback.
The conversion to the AsyncCall was necessary because legacy [close
handler callbacks] cannot be canceled once scheduled.
Marcos Mello [Tue, 23 Feb 2016 22:27:43 +0000 (11:27 +1300)]
Bug 3826: SMP compatibility with systemd
** These changes require capabilities changes specific to Squid-4 and
require systemd 209+
NOTE: 'squid -z' command does not yet support SMP with systemd.
Differences from the Squid-3 tools/systemd/squid.service:
- After=nss-lookup.target, for people running a local DNS server like BIND.
Since there is no requirement dependency, it is a NOP when no such
service is running.
- Type=forking and squid without -N in ExecStart: SMP now works.
- PIDFile=/var/run/squid.pid to tell systemd what pid is the main one. This
is actually optional with Squid 4, because systemd will consider its first
child as the main pid. But let's be safe. DEFAULT_PID_FILE could be used
here with proper autoconf/automake magic...
- ExecReload calls kill rather than 'squid -k reconfigure'. systemd already
knows the main pid.
- KillMode=mixed. The old KillMode=process sends SIGTERM (and SIGKILL after
TimeoutStopSec) only to main daemon process. 'mixed' OTOH sends SIGTERM
only to main process, but SIGKILL to all services' cgroup processes after
timeout. With 'mixed' systemd ensures if daemon shutdown fails it will
clean up all the remains. 'mixed' requires systemd >= 209.