On 01/10/2014 01:04 AM, Amos Jeffries wrote:
> On 2014-01-10 04:50, Tsantilas Christos wrote:
>> On 01/09/2014 12:07 AM, Amos Jeffries wrote:
>>> On 2014-01-09 07:30, Tsantilas Christos wrote:
>>>> This patch implement SSL session cache sharing across SMP workers using
>>>> shared memory. The following new squid configuration options added:
>>>>
>>>> - The "sslproxy_session_cache_size" option which sets the cache
>>>> size to
>>>> use for ssl session. Example usage:
>>>> sslproxy_session_cache_size 4 MB
>>>>
>>>> - The "sslproxy_session_ttl" option which defines the time in seconds
>>>> the ssl session is valid. Example usage:
>>>> sslproxy_session_ttl 600
>>>>
>>>> This patch also investigates the Ipc::MemMap class to help squid
>>>> developers implement shared caches for squid processes.
>>>>
>>>> This is a Measurement Factory project
>>
>> Some notes before proceed to any change in the patch. I need squid
>> developers opinions.
>>
>> If you compare the MemMap and StoreMap code you will see that they are
>> similar. The original goal was to keep the code similar in order to
>> merge these two classes to one in the future.
>>
>> This is why some of the variable documentations does not make sense for
>> this class, and this is why the sfileno is used instead of an unsigned
>> integer type.
>
> Thank you for explaining sfileno. Its seems weird but is okay. A code
> comment to explain would not go amiss.
> As for the code comments describing weirdness, please make them correct
> for the class as of *this* submission. They can (and should) be changed
> during that future merge to be appropriate for design decisions made
> during those changes which likely turn out to be different from what we
> currently assume. Making them correct for the now helps avoid people
> breaking the current API.
I tried to make most of the changes you are requests.
Also because StoreMap has many changes sinse this patch had developed, I
make some more changes to make StoreMap and MemMap similar.
>
>>
>> This is something I should mention when I posted the patch but this
>> patch implemented a long time ago, and I had forgot it. I just re-read
>> internal Measurement Factory mails about this. Sorry for this.
>>
>> I am suggesting to keep MemMap as is (with only the FlexibleArrays
>> fixes) just to have it similar to StoreMap class.
>>
>> In the future we should merge the Ipc::StoreMap and Ipc::MemMap to a
>> Ipc::SharedCache class. Then we can make more fixes here.
>>
>> Opinions?
>>
>
> From the earlier description I was thinking you were intending to make
> this MemMap a generic memory cache class in the sense that we could use
> it for the non-Store caching Squid does (ie auth credentials, helper
> results, DNS results) not just StoreMap HTTP objects. That is a base
> class we could really use to fix the outstanding non-SMP components.
This is the goal.
>
> With that assumption it makes more sense to make StoreMap inherit from
> this class and add the Store-specific bits on top. This class just doing
> generic cache management details (key/ttl/add/remove/update) in
> SMP-aware operations.
Yep.
>
> Given your plans, the request to separate SSL changes from MemMap
> addition is even more important to be done so that future Store updates
> do not result in someone inexperienced attempting to it themselves when
> back-porting a future Store change.
I am including the MemMap in this patch, just to fix it if required. I
will commit as separate patches.
>
> Most of the requests are styling and documentation fixes. They do not
> change with this extra detail, except the one about use of size_t can be
> ignored.
>
>>
>>>
>>> general issues:
>>>
>>> * Please do not add HERE macro in new code.
removed.
>>>
>>> * MemMap seems to be combining the concepts and operations of an indexed
>>> cache with timeouts. "map"'s do not have the same semantics. Perhaps
>>> calling it Ipc::SharedCache would be better.
I let the name as is for now. In the future, if it is possible we should
merge MemMap and StoreMap to an Ipc::SharedCache class, or make them
kids of a Ipc::SharedCache class.
>>>
>>> * Please finalize and merge the MemMap feature separately so it is not
>>> tied to the SSL changes.
I will post it as separate patches
>>>
>>>
>>> in src/ipc/MemMap.cc:
>>>
>>> * why is sfileno being used to references slots? It is a type
>>> specifically designed for operations matching inode filesystem
>>> semantics. It is dragging in size limitations which memory caches are
>>> perhaps better off without.
This is becaus StoreMap uses sfileno. Just to have similar interfaces
for two classes.
>>>
>>> * please implement the XXX notes about FlexibleArray
>>> - XXX also in .h
done
>>>
>>> * +Ipc::MemMap::openForWriting()
>>> - "XXX: the caller should do that" already seems to be implemented by
>>> commenting out the setKey(). Please remove the whole line if it is
>>> working as-is right now.
done
>>> -
>>>
>>> * Ipc::MemMap::abortIo() comment about caller is irrelevant and wrong
>>> (caller could be anything). The only relevant thing is what state the
>>> *slot* is in and the code explains that well enough.
>>> - Please remove that comment.
This method removed.
>>>
>>> * entryCount() / entryLimit() and possibly valid() should be unsigned
>>> type (size_t probably).
I did not change these two members, to have the same interface with
StoreMap class
>>>
>>>
>>> in src/ipc/MemMap.h:
>>>
>>> * Please update class members order/layout to match Squid-3 coding
>>> guidelines.
>>> - see
>>> http://wiki.squid-cache.org/Squid3CodingGuidelines#Class_declaration_layout
Yep. fixed now.
>>>
>>>
>>> * If MemMap is supposed to be used outside SSL why is it depending on
>>> macros named SSL_SESSION_* ?
>>> - please rename those to something more generic.
We have a problem here. To convert MemMap to a general class we need to
make MemMapSlot a template class with template parameters the key size
and store data size.
This is also requires convert the MemMap class to a template class.
For now I did the following:
- Renamed the SSL_SESSION_ID_SIZE and SSL_SESSION_MAX_SIZE to
MEMMAP_SLOT_KEY_SIZE and MEMMAP_SLOT_DATA_SIZE and I add some assertions
in support.cc related code to be sure that these defines are always
enough big to hold SSL shared session data.
I hope it is OK.
>>>
>>> * comment explaining class MemMap appears to have multiple sentences.
>>> Please use upper case and full stops. The doxygen output will run those
>>> lines into a single sentence if you dont write clearly.
I fix it a litle. Now it is a small comment but I hope it is enough.
>>>
>>> * openForWriting() comment "finds, reservers space for " is not clear.
>>> - what exactly is is saying anyway?
fixed
>>> - there is also a missing empty line between this function declaration
>>> and the next ones comment.
fixed
>>>
>>> * comment "notified before a readable entry is freed" for cleaner.
>>> Please clarify what cleaner is in relation to MemMap and how "notify"
>>> happens (callback? require class method?).
I fix the documentation here
>>>
>>> * please use SBuf instead of String for new variables (eg SBuf path)
ok.
>>>
>>> * Ipc::MemMapSlot::set( ... expireOn)
>>> - s/expireOn/expireAt/ - English weird grammar. time_t is treated as
>>> absolute value so 'at' (as opposed to offset in a scale which would be
>>> 'on').
fixed
>>>
>>> * How about moving waitingToBeFreed above the lock. That way the
>>> comments are more consistent with what the lock controls.
Do you mean on declaration members?
Now it is before lock.
>>>
>>> * please update documentation of State::Writeable. "transitions from
>>> Empty to Readable" does not explain anything unless one already knows
>>> what the state means.
>>> - perhapse Writeable should be described "in process of being written
>>> to, not Empty and not yet Readable". If so then it should also probably
>>> be renamed "Writing" to match the active semantic.
I synced the MemMap class to StoreMap class. The states and State::*
removed now.
>>>
>>>
>>> in src/main.cc:
>>>
>>> * the #if USE_SSL macro does not need adding twice in a row. Please
>>> combine the USE_SSL operations under one wrapper #if...#endif
>>>
ok fixed
>>>
>>> in src/ssl/support.cc:
>>>
>>> * store_session_cb() and remove_session_cb() should be static yes?
They can be static yes.
>>>
>>> * why is store_session_cb() always returning 0?
>>> - surely the absence of a session cache when the callback occurs should
>>> be an error.
If we return error in these cases will result to abort SSL connection.
This is wrong, we want to proceed with SSL negotiation even if we did
not store the session.
>>> - also the comment question should be marked with a TODO
I supose you mean the case we are failing to remove a session from
cache. I add a TODO on top of comment. However, looks that we do not
need to handle this case.
>>>
>>> * get_session_cb()
>>> - double-empty lines near the top of the function.
ok.
>>>
>>> * Ssl::initialize_session_cache()
>>> - the for loops seem to be doing a lot of useless walking in the case
>>> that "else SslSessionCache = NULL;". Please return immediately after
>>> setting that =NULL value.
ok fixed.
>>>
>>>
>>> Amos
>>>
>>>
>
SMP SSL session cache implementation
This patch implement SSL session cache sharing across SMP workers using shared
memory. The following new squid configuration options added:
- The "sslproxy_session_cache_size" option which sets the cache size to use
for ssl session. Example usage:
sslproxy_session_cache_size 4 MB
- The "sslproxy_session_ttl" option which defines the time in seconds the
ssl session is valid. Example usage:
sslproxy_session_ttl 600
This is a Measurement Factory project
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2014-01-03 10:32:53 +0000
+++ src/Makefile.am 2014-01-08 11:03:19 +0000
@@ -610,58 +610,58 @@
swap_log_op.cc
CLEANFILES += $(BUILT_SOURCES)
nodist_squid_SOURCES = \
$(DISKIO_GEN_SOURCE) \
$(BUILT_SOURCES)
squid_LDADD = \
$(AUTH_ACL_LIBS) \
ident/libident.la \
acl/libacls.la \
acl/libstate.la \
$(AUTH_LIBS) \
$(DISK_LIBS) \
acl/libapi.la \
base/libbase.la \
libsquid.la \
ip/libip.la \
fs/libfs.la \
+ $(SSL_LIBS) \
ipc/libipc.la \
mgr/libmgr.la \
anyp/libanyp.la \
comm/libcomm.la \
eui/libeui.la \
http/libsquid-http.la \
icmp/libicmp.la icmp/libicmp-core.la \
log/liblog.la \
format/libformat.la \
$(XTRA_OBJS) \
$(DISK_LINKOBJS) \
$(REPL_OBJS) \
$(DISK_OS_LIBS) \
$(CRYPTLIB) \
$(REGEXLIB) \
$(ADAPTATION_LIBS) \
$(ESI_LIBS) \
- $(SSL_LIBS) \
$(SNMP_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
$(SSLLIB) \
$(EPOLL_LIBS) \
$(MINGW_LIBS) \
$(KRB5LIBS) \
$(COMPAT_LIB) \
$(XTRA_LIBS)
squid_DEPENDENCIES = \
$(DISK_LIBS) \
$(DISK_LINKOBJS) \
$(REPL_OBJS) \
$(ADAPTATION_LIBS) \
$(ESI_LOCAL_LIBS) \
$(SSL_LIBS) \
$(AUTH_ACL_LIBS) \
ident/libident.la \
acl/libacls.la \
@@ -1776,43 +1776,43 @@
swap_log_op.cc
tests_testDiskIO_LDADD = \
http/libsquid-http.la \
SquidConfig.o \
CommCalls.o \
DnsLookupDetails.o \
ident/libident.la \
acl/libacls.la \
acl/libstate.la \
libsquid.la \
comm/libcomm.la \
anyp/libanyp.la \
ip/libip.la \
fs/libfs.la \
ipc/libipc.la \
$(REPL_OBJS) \
$(DISK_LIBS) \
$(DISK_OS_LIBS) \
acl/libapi.la \
mgr/libmgr.la \
+ $(SSL_LIBS) \
ipc/libipc.la \
base/libbase.la \
- $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
$(REGEXLIB) \
$(SQUID_CPPUNIT_LIBS) \
$(SSLLIB) \
$(COMPAT_LIB) \
$(XTRA_LIBS)
tests_testDiskIO_LDFLAGS = $(LIBADD_DL)
tests_testDiskIO_DEPENDENCIES = \
$(DISK_LIBS) \
$(SWAP_TEST_DS) \
$(SQUID_CPPUNIT_LA)
## Tests of the Even module.
tests_testEvent_SOURCES = \
AccessLogEntry.cc \
BodyPipe.cc \
CacheDigest.h \
@@ -2790,54 +2790,54 @@
urn.h \
urn.cc \
wccp2.h \
tests/stub_wccp2.cc \
whois.h \
tests/stub_whois.cc \
FadingCounter.cc \
$(WIN32_SOURCE) \
wordlist.h \
wordlist.cc
nodist_tests_testHttpRequest_SOURCES = \
$(BUILT_SOURCES)
tests_testHttpRequest_LDADD = \
ident/libident.la \
acl/libacls.la \
acl/libstate.la \
acl/libapi.la \
libsquid.la \
ip/libip.la \
fs/libfs.la \
+ $(SSL_LIBS) \
ipc/libipc.la \
base/libbase.la \
mgr/libmgr.la \
anyp/libanyp.la \
$(SNMP_LIBS) \
icmp/libicmp.la icmp/libicmp-core.la \
comm/libcomm.la \
log/liblog.la \
format/libformat.la \
http/libsquid-http.la \
$(REPL_OBJS) \
$(ADAPTATION_LIBS) \
$(ESI_LIBS) \
- $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
$(DISK_OS_LIBS) \
$(REGEXLIB) \
$(SQUID_CPPUNIT_LIBS) \
$(SQUID_CPPUNIT_LA) \
$(SSLLIB) \
$(KRB5LIBS) \
$(COMPAT_LIB) \
$(XTRA_LIBS)
tests_testHttpRequest_LDFLAGS = $(LIBADD_DL)
tests_testHttpRequest_DEPENDENCIES = \
$(REPL_OBJS) \
$(SQUID_CPPUNIT_LA)
## why so many sources? well httpHeaderTools requites ACLChecklist & friends.
## first line - what we are testing.
tests_testStore_SOURCES= \
CacheDigest.h \
@@ -2977,43 +2977,43 @@
wordlist.h \
wordlist.cc
nodist_tests_testStore_SOURCES= \
$(TESTSOURCES) \
SquidMath.cc \
SquidMath.h \
swap_log_op.cc
tests_testStore_LDADD= \
http/libsquid-http.la \
ident/libident.la \
acl/libacls.la \
acl/libstate.la \
acl/libapi.la \
base/libbase.la \
libsquid.la \
ip/libip.la \
fs/libfs.la \
mgr/libmgr.la \
+ $(SSL_LIBS) \
ipc/libipc.la \
anyp/libanyp.la \
- $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
$(REGEXLIB) \
$(SQUID_CPPUNIT_LIBS) \
$(SSLLIB) \
CommCalls.o \
DnsLookupDetails.o \
$(COMPAT_LIB) \
$(XTRA_LIBS)
tests_testStore_LDFLAGS = $(LIBADD_DL)
tests_testStore_DEPENDENCIES = \
$(SQUID_CPPUNIT_LA)
## string needs mem.cc.
## mem.cc needs ClientInfo.h
## libsquid pulls in SquidConfig and children. stub them.
tests_testString_SOURCES = \
ClientInfo.h \
Mem.h \
@@ -3213,42 +3213,42 @@
SquidMath.cc \
SquidMath.h \
swap_log_op.cc
tests_testUfs_LDADD = \
http/libsquid-http.la \
CommCalls.o \
DnsLookupDetails.o \
ident/libident.la \
acl/libacls.la \
acl/libstate.la \
acl/libapi.la \
libsquid.la \
ip/libip.la \
fs/libfs.la \
mgr/libmgr.la \
$(REPL_OBJS) \
acl/libacls.la \
$(DISK_LIBS) \
$(DISK_OS_LIBS) \
acl/libapi.la \
- ipc/libipc.la \
$(SSL_LIBS) \
+ ipc/libipc.la \
comm/libcomm.la \
anyp/libanyp.la \
base/libbase.la \
ip/libip.la \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
$(REGEXLIB) \
$(SQUID_CPPUNIT_LIBS) \
$(SSLLIB) \
$(COMPAT_LIB) \
$(XTRA_LIBS)
tests_testUfs_LDFLAGS = $(LIBADD_DL)
tests_testUfs_DEPENDENCIES = \
$(SWAP_TEST_DS)
check_PROGRAMS += testRefCount
testRefCount_SOURCES= \
base/Lock.h \
base/RefCount.h \
@@ -3394,43 +3394,44 @@
nodist_tests_testRock_SOURCES = \
$(DISKIO_GEN_SOURCE) \
swap_log_op.cc \
SquidMath.cc \
SquidMath.h \
$(TESTSOURCES)
tests_testRock_LDADD = \
http/libsquid-http.la \
libsquid.la \
comm/libcomm.la \
anyp/libanyp.la \
ip/libip.la \
fs/libfs.la \
$(COMMON_LIBS) \
$(REPL_OBJS) \
$(DISK_LIBS) \
$(DISK_OS_LIBS) \
acl/libacls.la \
acl/libapi.la \
acl/libstate.la \
+ eui/libeui.la \
+ $(SSL_LIBS) \
ipc/libipc.la \
base/libbase.la \
- $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
$(REGEXLIB) \
$(SQUID_CPPUNIT_LIBS) \
$(SSLLIB) \
$(COMPAT_LIB) \
$(XTRA_LIBS)
tests_testRock_LDFLAGS = $(INCLUDES) $(LIBADD_DL)
tests_testRock_DEPENDENCIES = \
$(SWAP_TEST_DS)
## Tests of the URL module.
## TODO: Trim this down once the insanity is over.
tests_testURL_SOURCES = \
AccessLogEntry.cc \
BodyPipe.cc \
cache_cf.h \
AuthReg.h \
YesNoNone.h \
@@ -3630,53 +3631,53 @@
whois.h \
tests/stub_whois.cc \
FadingCounter.cc \
$(WIN32_SOURCE) \
wordlist.h \
wordlist.cc
nodist_tests_testURL_SOURCES = \
$(BUILT_SOURCES)
tests_testURL_LDADD = \
http/libsquid-http.la \
anyp/libanyp.la \
ident/libident.la \
acl/libacls.la \
eui/libeui.la \
acl/libstate.la \
acl/libapi.la \
base/libbase.la \
libsquid.la \
ip/libip.la \
fs/libfs.la \
+ $(SSL_LIBS) \
ipc/libipc.la \
mgr/libmgr.la \
$(SNMP_LIBS) \
icmp/libicmp.la icmp/libicmp-core.la \
comm/libcomm.la \
log/liblog.la \
$(DISK_OS_LIBS) \
format/libformat.la \
$(REGEXLIB) \
$(REPL_OBJS) \
$(ADAPTATION_LIBS) \
$(ESI_LIBS) \
- $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
$(COMPAT_LIB) \
$(SQUID_CPPUNIT_LIBS) \
$(SQUID_CPPUNIT_LA) \
$(SSLLIB) \
$(KRB5LIBS) \
$(COMPAT_LIB) \
$(XTRA_LIBS)
tests_testURL_LDFLAGS = $(LIBADD_DL)
tests_testURL_DEPENDENCIES = \
$(REPL_OBJS) \
$(SQUID_CPPUNIT_LA)
tests_testSBuf_SOURCES= \
tests/testSBuf.h \
tests/testSBuf.cc \
tests/testMain.cc \
tests/SBufFindTest.h \
=== modified file 'src/SquidConfig.h'
--- src/SquidConfig.h 2014-01-01 19:20:49 +0000
+++ src/SquidConfig.h 2014-01-08 11:01:04 +0000
@@ -475,40 +475,42 @@
HeaderWithAclList *request_header_add;
///note
Notes notes;
char *coredump_dir;
char *chroot_dir;
#if USE_CACHE_DIGESTS
struct {
int bits_per_entry;
time_t rebuild_period;
time_t rewrite_period;
size_t swapout_chunk_size;
int rebuild_chunk_percentage;
} digest;
#endif
#if USE_SSL
struct {
int unclean_shutdown;
char *ssl_engine;
+ int session_ttl;
+ size_t sessionCacheSize;
} SSL;
#endif
wordlist *ext_methods;
struct {
int high_rptm;
int high_pf;
size_t high_memory;
} warnings;
char *store_dir_select_algorithm;
int sleep_after_fork; /* microseconds */
time_t minimum_expiry_time; /* seconds */
external_acl *externalAclHelperList;
#if USE_SSL
struct {
char *cert;
char *key;
=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2014-01-05 02:56:31 +0000
+++ src/cf.data.pre 2014-01-08 11:01:04 +0000
@@ -2359,40 +2359,58 @@
NAME: sslproxy_cafile
IFDEF: USE_SSL
DEFAULT: none
LOC: Config.ssl_client.cafile
TYPE: string
DOC_START
file containing CA certificates to use when verifying server
certificates while proxying https:// URLs
DOC_END
NAME: sslproxy_capath
IFDEF: USE_SSL
DEFAULT: none
LOC: Config.ssl_client.capath
TYPE: string
DOC_START
directory containing CA certificates to use when verifying
server certificates while proxying https:// URLs
DOC_END
+NAME: sslproxy_session_ttl
+IFDEF: USE_SSL
+DEFAULT: 300
+LOC: Config.SSL.session_ttl
+TYPE: int
+DOC_START
+ Sets the timeout value for SSL sessions
+DOC_END
+
+NAME: sslproxy_session_cache_size
+IFDEF: USE_SSL
+DEFAULT: 2 MB
+LOC: Config.SSL.sessionCacheSize
+TYPE: b_size_t
+DOC_START
+ Sets the cache size to use for ssl session
+DOC_END
+
NAME: ssl_bump
IFDEF: USE_SSL
TYPE: sslproxy_ssl_bump
LOC: Config.accessList.ssl_bump
DEFAULT_DOC: Does not bump unless rules are present in squid.conf
DEFAULT: none
DOC_START
This option is consulted when a CONNECT request is received on
an http_port (or a new connection is intercepted at an
https_port), provided that port was configured with an ssl-bump
flag. The subsequent data on the connection is either treated as
HTTPS and decrypted OR tunneled at TCP level without decryption,
depending on the first bumping "mode" which ACLs match.
ssl_bump <mode> [!]acl ...
The following bumping modes are supported:
client-first
Allow bumping of the connection. Establish a secure connection
=== modified file 'src/ipc/Makefile.am'
--- src/ipc/Makefile.am 2013-01-17 04:25:35 +0000
+++ src/ipc/Makefile.am 2014-01-08 10:17:42 +0000
@@ -1,35 +1,37 @@
include $(top_srcdir)/src/Common.am
include $(top_srcdir)/src/TestHeaders.am
noinst_LTLIBRARIES = libipc.la
libipc_la_SOURCES = \
AtomicWord.cc \
AtomicWord.h \
FdNotes.cc \
FdNotes.h \
Kid.cc \
Kid.h \
Kids.cc \
Kids.h \
Messages.h \
+ MemMap.cc \
+ MemMap.h \
Queue.cc \
Queue.h \
ReadWriteLock.cc \
ReadWriteLock.h \
StartListening.cc \
StartListening.h \
StoreMap.cc \
StoreMap.h \
StrandCoord.cc \
StrandCoord.h \
StrandCoords.h \
StrandSearch.cc \
StrandSearch.h \
SharedListen.cc \
SharedListen.h \
TypedMsgHdr.cc \
TypedMsgHdr.h \
Coordinator.cc \
Coordinator.h \
UdsOp.cc \
=== added file 'src/ipc/MemMap.cc'
--- src/ipc/MemMap.cc 1970-01-01 00:00:00 +0000
+++ src/ipc/MemMap.cc 2014-01-10 16:28:32 +0000
@@ -0,0 +1,330 @@
+/*
+ * DEBUG: section 54 Interprocess Communication
+ */
+
+#include "squid.h"
+#include "ipc/MemMap.h"
+#include "store_key_md5.h"
+#include "tools.h"
+
+Ipc::MemMap::MemMap(const char *const aPath): cleaner(NULL), path(aPath),
+ shared(shm_old(Shared)(aPath))
+{
+ assert(shared->limit > 0); // we should not be created otherwise
+ debugs(54, 5, "attached map [" << path << "] created: " <<
+ shared->limit);
+}
+
+Ipc::MemMap::Owner *
+Ipc::MemMap::Init(const char *const path, const int limit, const size_t extrasSize)
+{
+ assert(limit > 0); // we should not be created otherwise
+ Owner *const owner = shm_new(Shared)(path, limit, extrasSize);
+ debugs(54, 5, "new map [" << path << "] created: " << limit);
+ return owner;
+}
+
+Ipc::MemMap::Owner *
+Ipc::MemMap::Init(const char *const path, const int limit)
+{
+ return Init(path, limit, 0);
+}
+
+
+Ipc::MemMap::Slot *
+Ipc::MemMap::openForWriting(const cache_key *const key, sfileno &fileno)
+{
+ debugs(54, 5, "trying to open slot for key " << storeKeyText(key)
+ << " for writing in map [" << path << ']');
+ const int idx = slotIndexByKey(key);
+
+ if (Slot *slot = openForWritingAt(idx)) {
+ fileno = idx;
+ return slot;
+ }
+
+ return NULL;
+}
+
+Ipc::MemMap::Slot *
+Ipc::MemMap::openForWritingAt(const sfileno fileno, bool overwriteExisting)
+{
+ Slot &s = shared->slots[fileno];
+ ReadWriteLock &lock = s.lock;
+
+ if (lock.lockExclusive()) {
+ assert(s.writing() && !s.reading());
+
+ // bail if we cannot empty this position
+ if (!s.waitingToBeFreed && !s.empty() && !overwriteExisting) {
+ lock.unlockExclusive();
+ debugs(54, 5, "cannot open existing entry " << fileno <<
+ " for writing " << path);
+ return NULL;
+ }
+
+ // free if the entry was used, keeping the entry locked
+ if (s.waitingToBeFreed || !s.empty())
+ freeLocked(s, true);
+
+ assert(s.empty());
+ ++shared->count;
+
+ debugs(54, 5, "opened slot at " << fileno <<
+ " for writing in map [" << path << ']');
+ return &s; // and keep the entry locked
+ }
+
+ debugs(54, 5, "failed to open slot at " << fileno <<
+ " for writing in map [" << path << ']');
+ return NULL;
+}
+
+void
+Ipc::MemMap::closeForWriting(const sfileno fileno, bool lockForReading)
+{
+ debugs(54, 5, "closing slot at " << fileno << " for writing and "
+ "openning for reading in map [" << path << ']');
+ assert(valid(fileno));
+ Slot &s = shared->slots[fileno];
+ assert(s.writing());
+ if (lockForReading)
+ s.lock.switchExclusiveToShared();
+ else
+ s.lock.unlockExclusive();
+}
+
+/// terminate writing the entry, freeing its slot for others to use
+void
+Ipc::MemMap::abortWriting(const sfileno fileno)
+{
+ debugs(54, 5, "abort writing slot at " << fileno <<
+ " in map [" << path << ']');
+ assert(valid(fileno));
+ Slot &s = shared->slots[fileno];
+ assert(s.writing());
+ freeLocked(s, false);
+}
+
+const Ipc::MemMap::Slot *
+Ipc::MemMap::peekAtReader(const sfileno fileno) const
+{
+ assert(valid(fileno));
+ const Slot &s = shared->slots[fileno];
+ if (s.reading())
+ return &s; // immediate access by lock holder so no locking
+ if (s.writing())
+ return NULL; // cannot read the slot when it is being written
+ assert(false); // must be locked for reading or writing
+ return NULL;
+}
+
+void
+Ipc::MemMap::free(const sfileno fileno)
+{
+ debugs(54, 5, "marking slot at " << fileno << " to be freed in"
+ " map [" << path << ']');
+
+ assert(valid(fileno));
+ Slot &s = shared->slots[fileno];
+
+ if (s.lock.lockExclusive())
+ freeLocked(s, false);
+ else
+ s.waitingToBeFreed = true; // mark to free it later
+}
+
+const Ipc::MemMap::Slot *
+Ipc::MemMap::openForReading(const cache_key *const key, sfileno &fileno)
+{
+ debugs(54, 5, "trying to open slot for key " << storeKeyText(key)
+ << " for reading in map [" << path << ']');
+ const int idx = slotIndexByKey(key);
+ if (const Slot *slot = openForReadingAt(idx)) {
+ if (slot->sameKey(key)) {
+ fileno = idx;
+ debugs(54, 5, "opened slot at " << fileno << " for key "
+ << storeKeyText(key) << " for reading in map [" << path <<
+ ']');
+ return slot; // locked for reading
+ }
+ slot->lock.unlockShared();
+ }
+ debugs(54, 5, "failed to open slot for key " << storeKeyText(key)
+ << " for reading in map [" << path << ']');
+ return NULL;
+}
+
+const Ipc::MemMap::Slot *
+Ipc::MemMap::openForReadingAt(const sfileno fileno)
+{
+ debugs(54, 5, "trying to open slot at " << fileno << " for "
+ "reading in map [" << path << ']');
+ assert(valid(fileno));
+ Slot &s = shared->slots[fileno];
+
+ if (!s.lock.lockShared()) {
+ debugs(54, 5, "failed to lock slot at " << fileno << " for "
+ "reading in map [" << path << ']');
+ return NULL;
+ }
+
+ if (s.empty()) {
+ s.lock.unlockShared();
+ debugs(54, 7, "empty slot at " << fileno << " for "
+ "reading in map [" << path << ']');
+ return NULL;
+ }
+
+ if (s.waitingToBeFreed) {
+ s.lock.unlockShared();
+ debugs(54, 7, "dirty slot at " << fileno << " for "
+ "reading in map [" << path << ']');
+ return NULL;
+ }
+
+ debugs(54, 5, "opened slot at " << fileno << " for reading in"
+ " map [" << path << ']');
+ return &s;
+}
+
+void
+Ipc::MemMap::closeForReading(const sfileno fileno)
+{
+ debugs(54, 5, "closing slot at " << fileno << " for reading in "
+ "map [" << path << ']');
+ assert(valid(fileno));
+ Slot &s = shared->slots[fileno];
+ assert(s.reading());
+ s.lock.unlockShared();
+}
+
+int
+Ipc::MemMap::entryLimit() const
+{
+ return shared->limit;
+}
+
+int
+Ipc::MemMap::entryCount() const
+{
+ return shared->count;
+}
+
+bool
+Ipc::MemMap::full() const
+{
+ return entryCount() >= entryLimit();
+}
+
+void
+Ipc::MemMap::updateStats(ReadWriteLockStats &stats) const
+{
+ for (int i = 0; i < shared->limit; ++i)
+ shared->slots[i].lock.updateStats(stats);
+}
+
+bool
+Ipc::MemMap::valid(const int pos) const
+{
+ return 0 <= pos && pos < entryLimit();
+}
+
+static
+unsigned int
+hash_key(const unsigned char *data, unsigned int len, unsigned int hashSize)
+{
+ unsigned int n;
+ unsigned int j;
+ for(j = 0, n = 0; j < len; j++ ) {
+ n ^= 271 * *data;
+ ++data;
+ }
+ return (n ^ (j * 271)) % hashSize;
+}
+
+int
+Ipc::MemMap::slotIndexByKey(const cache_key *const key) const
+{
+ const unsigned char *k = reinterpret_cast<const unsigned char *>(key);
+ return hash_key(k, MEMMAP_SLOT_KEY_SIZE, shared->limit);
+}
+
+Ipc::MemMap::Slot &
+Ipc::MemMap::slotByKey(const cache_key *const key)
+{
+ return shared->slots[slotIndexByKey(key)];
+}
+
+/// unconditionally frees the already exclusively locked slot and releases lock
+void
+Ipc::MemMap::freeLocked(Slot &s, bool keepLocked)
+{
+ if (!s.empty() && cleaner)
+ cleaner->noteFreeMapSlot(&s - shared->slots.raw());
+
+ s.waitingToBeFreed = false;
+ memset(s.key, 0, sizeof(s.key));
+ if (!keepLocked)
+ s.lock.unlockExclusive();
+ --shared->count;
+ debugs(54, 5, "freed slot at " << (&s - shared->slots.raw()) <<
+ " in map [" << path << ']');
+}
+
+/* Ipc::MemMapSlot */
+Ipc::MemMapSlot::MemMapSlot()
+{
+ memset(key, 0, sizeof(key));
+ memset(p, 0, sizeof(p));
+ pSize = 0;
+}
+
+void
+Ipc::MemMapSlot::set(const unsigned char *aKey, const void *block, size_t blockSize, time_t expireAt)
+{
+ memcpy(key, aKey, sizeof(key));
+ if (block)
+ memcpy(p, block, blockSize);
+ pSize = blockSize;
+ expire = expireAt;
+}
+
+bool
+Ipc::MemMapSlot::sameKey(const cache_key *const aKey) const
+{
+ return (memcmp(key, aKey, sizeof(key)) == 0);
+}
+
+bool
+Ipc::MemMapSlot::empty() const
+{
+ for (unsigned char const*u = key; u < key + sizeof(key); ++u) {
+ if (*u)
+ return false;
+ }
+ return true;
+}
+
+/* Ipc::MemMap::Shared */
+
+Ipc::MemMap::Shared::Shared(const int aLimit, const size_t anExtrasSize):
+ limit(aLimit), extrasSize(anExtrasSize), count(0), slots(aLimit)
+{
+}
+
+Ipc::MemMap::Shared::~Shared()
+{
+}
+
+size_t
+Ipc::MemMap::Shared::sharedMemorySize() const
+{
+ return SharedMemorySize(limit, extrasSize);
+}
+
+size_t
+Ipc::MemMap::Shared::SharedMemorySize(const int limit, const size_t extrasSize)
+{
+ return sizeof(Shared) + limit * (sizeof(Slot) + extrasSize);
+}
=== added file 'src/ipc/MemMap.h'
--- src/ipc/MemMap.h 1970-01-01 00:00:00 +0000
+++ src/ipc/MemMap.h 2014-01-10 17:05:57 +0000
@@ -0,0 +1,140 @@
+#ifndef SQUID_IPC_STORE_MAP_H
+#define SQUID_IPC_STORE_MAP_H
+
+#include "Debug.h"
+#include "ipc/mem/FlexibleArray.h"
+#include "ipc/mem/Pointer.h"
+#include "ipc/ReadWriteLock.h"
+#include "SBuf.h"
+#include "tools.h"
+#include "typedefs.h"
+
+namespace Ipc
+{
+
+// The MEMMAP_SLOT_KEY_SIZE and MEMMAP_SLOT_DATA_SIZE must be enough big
+// to hold cached keys and data. Currently MemMap used only to store SSL
+// shared session data which have keys of 32bytes and at most 10K data
+#define MEMMAP_SLOT_KEY_SIZE 32
+#define MEMMAP_SLOT_DATA_SIZE 10*1024
+
+/// a MemMap basic element, holding basic shareable memory block info
+class MemMapSlot
+{
+public:
+ MemMapSlot();
+ size_t size() const {return sizeof(MemMapSlot);}
+ size_t keySize() const {return sizeof(key);}
+ bool sameKey(const cache_key *const aKey) const;
+ void set(const unsigned char *aKey, const void *block, size_t blockSize, time_t expire = 0);
+ bool empty() const;
+ bool reading() const { return lock.readers; }
+ bool writing() const { return lock.writing; }
+
+ Atomic::WordT<uint8_t> waitingToBeFreed; ///< may be accessed w/o a lock
+ mutable ReadWriteLock lock; ///< protects slot data below
+ unsigned char key[MEMMAP_SLOT_KEY_SIZE]; ///< The entry key
+ unsigned char p[MEMMAP_SLOT_DATA_SIZE]; ///< The memory block;
+ size_t pSize;
+ time_t expire;
+};
+
+class MemMapCleaner;
+
+/// A map of MemMapSlots indexed by their keys, with read/write slot locking.
+class MemMap
+{
+public:
+ typedef MemMapSlot Slot;
+
+ /// data shared across maps in different processes
+ class Shared
+ {
+ public:
+ Shared(const int aLimit, const size_t anExtrasSize);
+ size_t sharedMemorySize() const;
+ static size_t SharedMemorySize(const int limit, const size_t anExtrasSize);
+ ~Shared();
+
+ const int limit; ///< maximum number of map slots
+ const size_t extrasSize; ///< size of slot extra data
+ Atomic::Word count; ///< current number of map slots
+ Ipc::Mem::FlexibleArray<Slot> slots; ///< storage
+ };
+
+public:
+ typedef Mem::Owner<Shared> Owner;
+
+ /// initialize shared memory
+ static Owner *Init(const char *const path, const int limit);
+
+ MemMap(const char *const aPath);
+
+ /// finds, locks and return a slot for an empty key position,
+ /// erasing the old entry (if any)
+ Slot *openForWriting(const cache_key *const key, sfileno &fileno);
+
+ /// locks and returns a slot for the empty fileno position; if
+ /// overwriteExisting is false and the position is not empty, returns nil
+ Slot *openForWritingAt(sfileno fileno, bool overwriteExisting = true);
+
+ /// successfully finish writing the entry
+ void closeForWriting(const sfileno fileno, bool lockForReading = false);
+
+ /// only works on locked entries; returns nil unless the slot is readable
+ const Slot *peekAtReader(const sfileno fileno) const;
+
+ /// mark the slot as waiting to be freed and, if possible, free it
+ void free(const sfileno fileno);
+
+ /// open slot for reading, increments read level
+ const Slot *openForReading(const cache_key *const key, sfileno &fileno);
+
+ /// open slot for reading, increments read level
+ const Slot *openForReadingAt(const sfileno fileno);
+
+ /// close slot after reading, decrements read level
+ void closeForReading(const sfileno fileno);
+
+ bool full() const; ///< there are no empty slots left
+ bool valid(const int n) const; ///< whether n is a valid slot coordinate
+ int entryCount() const; ///< number of used slots
+ int entryLimit() const; ///< maximum number of slots that can be used
+
+ /// adds approximate current stats to the supplied ones
+ void updateStats(ReadWriteLockStats &stats) const;
+
+ /// The cleaner MemMapCleaner::noteFreeMapSlot method called when a
+ /// readable entry is freed.
+ MemMapCleaner *cleaner;
+
+protected:
+ static Owner *Init(const char *const path, const int limit, const size_t extrasSize);
+
+ const SBuf path; ///< cache_dir path, used for logging
+ Mem::Pointer<Shared> shared;
+ int ttl;
+
+private:
+ int slotIndexByKey(const cache_key *const key) const;
+ Slot &slotByKey(const cache_key *const key);
+
+ Slot *openForReading(Slot &s);
+ void abortWriting(const sfileno fileno);
+ void freeIfNeeded(Slot &s);
+ void freeLocked(Slot &s, bool keepLocked);
+};
+
+/// API for adjusting external state when dirty map slot is being freed
+class MemMapCleaner
+{
+public:
+ virtual ~MemMapCleaner() {}
+
+ /// adjust slot-linked state before a locked Readable slot is erased
+ virtual void noteFreeMapSlot(const sfileno slotId) = 0;
+};
+
+} // namespace Ipc
+
+#endif /* SQUID_IPC_STORE_MAP_H */
=== modified file 'src/main.cc'
--- src/main.cc 2013-11-29 19:47:54 +0000
+++ src/main.cc 2014-01-10 17:16:18 +0000
@@ -1040,40 +1040,43 @@
}
#endif
if (!configured_once)
disk_init(); /* disk_init must go before ipcache_init() */
ipcache_init();
fqdncache_init();
parseEtcHosts();
dnsInit();
#if USE_SSL_CRTD
Ssl::Helper::GetInstance()->Init();
#endif
#if USE_SSL
+ if (!configured_once)
+ Ssl::initialize_session_cache();
+
if (Ssl::CertValidationHelper::GetInstance())
Ssl::CertValidationHelper::GetInstance()->Init();
#endif
redirectInit();
#if USE_AUTH
authenticateInit(&Auth::TheConfig);
#endif
externalAclInit();
httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
httpReplyInitModule(); /* must go before accepting replies */
errorInitialize();
accessLogInit();
#if ICAP_CLIENT
icapLogOpen();
=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc 2013-10-25 00:13:46 +0000
+++ src/ssl/support.cc 2014-01-10 17:26:38 +0000
@@ -24,52 +24,58 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
*/
#include "squid.h"
/* MS Visual Studio Projects are monolithic, so we need the following
* #if to exclude the SSL code from compile process when not needed.
*/
#if USE_SSL
#include "acl/FilledChecklist.h"
#include "anyp/PortCfg.h"
#include "fde.h"
+#include "ipc/MemMap.h"
#include "globals.h"
#include "SquidConfig.h"
+#include "SquidTime.h"
#include "ssl/Config.h"
#include "ssl/ErrorDetail.h"
#include "ssl/gadgets.h"
#include "ssl/support.h"
#include "URL.h"
#if HAVE_ERRNO_H
#include <errno.h>
#endif
+static void setSessionCallbacks(SSL_CTX *ctx);
+Ipc::MemMap *SslSessionCache = NULL;
+const char *SslSessionCacheName = "ssl_session_cache";
+
const char *Ssl::BumpModeStr[] = {
"none",
"client-first",
"server-first",
NULL
};
/**
\defgroup ServerProtocolSSLInternal Server-Side SSL Internals
\ingroup ServerProtocolSSLAPI
*/
/// \ingroup ServerProtocolSSLInternal
static int
ssl_ask_password_cb(char *buf, int size, int rwflag, void *userdata)
{
FILE *in;
int len = 0;
char cmdline[1024];
@@ -716,41 +722,40 @@
if (Config.SSL.ssl_engine) {
ENGINE *e;
if (!(e = ENGINE_by_id(Config.SSL.ssl_engine))) {
fatalf("Unable to find SSL engine '%s'\n", Config.SSL.ssl_engine);
}
if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
int ssl_error = ERR_get_error();
fatalf("Failed to initialise SSL engine: %s\n",
ERR_error_string(ssl_error, NULL));
}
}
#else
if (Config.SSL.ssl_engine) {
fatalf("Your OpenSSL has no SSL engine support\n");
}
#endif
-
}
ssl_ex_index_server = SSL_get_ex_new_index(0, (void *) "server", NULL, NULL, NULL);
ssl_ctx_ex_index_dont_verify_domain = SSL_CTX_get_ex_new_index(0, (void *) "dont_verify_domain", NULL, NULL, NULL);
ssl_ex_index_cert_error_check = SSL_get_ex_new_index(0, (void *) "cert_error_check", NULL, &ssl_dupAclChecklist, &ssl_freeAclChecklist);
ssl_ex_index_ssl_error_detail = SSL_get_ex_new_index(0, (void *) "ssl_error_detail", NULL, NULL, &ssl_free_ErrorDetail);
ssl_ex_index_ssl_peeked_cert = SSL_get_ex_new_index(0, (void *) "ssl_peeked_cert", NULL, NULL, &ssl_free_X509);
ssl_ex_index_ssl_errors = SSL_get_ex_new_index(0, (void *) "ssl_errors", NULL, NULL, &ssl_free_SslErrors);
ssl_ex_index_ssl_cert_chain = SSL_get_ex_new_index(0, (void *) "ssl_cert_chain", NULL, NULL, &ssl_free_CertChain);
ssl_ex_index_ssl_validation_counter = SSL_get_ex_new_index(0, (void *) "ssl_validation_counter", NULL, NULL, &ssl_free_int);
}
/// \ingroup ServerProtocolSSLInternal
static int
ssl_load_crl(SSL_CTX *sslContext, const char *CRLfile)
{
X509_STORE *st = SSL_CTX_get_cert_store(sslContext);
X509_CRL *crl;
BIO *in = BIO_new_file(CRLfile, "r");
int count = 0;
@@ -897,40 +902,42 @@
#if X509_V_FLAG_CRL_CHECK
if (port.sslContextFlags & SSL_FLAG_VERIFY_CRL_ALL)
X509_STORE_set_flags(SSL_CTX_get_cert_store(sslContext), X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
else if (port.sslContextFlags & SSL_FLAG_VERIFY_CRL)
X509_STORE_set_flags(SSL_CTX_get_cert_store(sslContext), X509_V_FLAG_CRL_CHECK);
#endif
} else {
debugs(83, 9, "Not requiring any client certificates");
SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
}
if (port.dhParams.get()) {
SSL_CTX_set_tmp_dh(sslContext, port.dhParams.get());
}
if (port.sslContextFlags & SSL_FLAG_DONT_VERIFY_DOMAIN)
SSL_CTX_set_ex_data(sslContext, ssl_ctx_ex_index_dont_verify_domain, (void *) -1);
+ setSessionCallbacks(sslContext);
+
return true;
}
SSL_CTX *
sslCreateServerContext(AnyP::PortCfg &port)
{
int ssl_error;
SSL_CTX *sslContext;
const char *keyfile, *certfile;
certfile = port.cert;
keyfile = port.key;
ssl_initialize();
if (!keyfile)
keyfile = certfile;
if (!certfile)
certfile = keyfile;
@@ -1656,21 +1663,216 @@
Ssl::CertError &
Ssl::CertError::operator = (const CertError &old)
{
code = old.code;
cert.resetAndLock(old.cert.get());
return *this;
}
bool
Ssl::CertError::operator == (const CertError &ce) const
{
return code == ce.code && cert.get() == ce.cert.get();
}
bool
Ssl::CertError::operator != (const CertError &ce) const
{
return code != ce.code || cert.get() != ce.cert.get();
}
+static int
+store_session_cb(SSL *ssl, SSL_SESSION *session)
+{
+ if (!SslSessionCache)
+ return 0;
+
+ debugs(83, 5, "Request to store SSL Session ");
+
+ SSL_SESSION_set_timeout(session, Config.SSL.session_ttl);
+
+ unsigned char *id = session->session_id;
+ unsigned int idlen = session->session_id_length;
+ unsigned char key[MEMMAP_SLOT_KEY_SIZE];
+ // Session ids are of size 32bytes. They should always fit to a
+ // MemMap::Slot::key
+ assert(idlen <= MEMMAP_SLOT_KEY_SIZE);
+ memset(key, 0, sizeof(key));
+ memcpy(key, id, idlen);
+ int pos;
+ Ipc::MemMap::Slot *slotW = SslSessionCache->openForWriting((const cache_key*)key, pos);
+ if (slotW) {
+ int lenRequired = i2d_SSL_SESSION(session, NULL);
+ if (lenRequired < MEMMAP_SLOT_DATA_SIZE) {
+ unsigned char *p = (unsigned char *)slotW->p;
+ lenRequired = i2d_SSL_SESSION(session, &p);
+ slotW->set(key, NULL, lenRequired, squid_curtime + Config.SSL.session_ttl);
+ }
+ SslSessionCache->closeForWriting(pos);
+ debugs(83, 5, "wrote an ssl session entry of size " << lenRequired << " at pos " << pos);
+ }
+ return 0;
+}
+
+static void
+remove_session_cb(SSL_CTX *, SSL_SESSION *sessionID)
+{
+ if (!SslSessionCache)
+ return ;
+
+ debugs(83, 5, "Request to remove corrupted or not valid SSL Session ");
+ int pos;
+ Ipc::MemMap::Slot const *slot = SslSessionCache->openForReading((const cache_key*)sessionID, pos);
+ if (slot == NULL)
+ return;
+ SslSessionCache->closeForReading(pos);
+ // TODO:
+ // What if we are not able to remove the session?
+ // Maybe schedule a job to remove it later?
+ // For now we just have an invalid entry in cache until will be expired
+ // The openSSL will reject it when we try to use it
+ SslSessionCache->free(pos);
+}
+
+static SSL_SESSION *
+get_session_cb(SSL *, unsigned char *sessionID, int len, int *copy)
+{
+ if (!SslSessionCache)
+ return NULL;
+
+ SSL_SESSION *session = NULL;
+ const unsigned int *p;
+ p = (unsigned int *)sessionID;
+ debugs(83, 5, "Request to search for SSL Session of len:" <<
+ len << p[0] << ":" << p[1]);
+
+ int pos;
+ Ipc::MemMap::Slot const *slot = SslSessionCache->openForReading((const cache_key*)sessionID, pos);
+ if (slot != NULL) {
+ if (slot->expire > squid_curtime) {
+ const unsigned char *ptr = slot->p;
+ session = d2i_SSL_SESSION(NULL, &ptr, slot->pSize);
+ debugs(83, 5, "Session retrieved from cache at pos " << pos);
+ } else
+ debugs(83, 5, "Session in cache expired");
+ SslSessionCache->closeForReading(pos);
+ }
+
+ if (!session)
+ debugs(83, 5, "Failed to retrieved from cache\n");
+
+ // With the parameter copy the callback can require the SSL engine
+ // to increment the reference count of the SSL_SESSION object, Normally
+ // the reference count is not incremented and therefore the session must
+ // not be explicitly freed with SSL_SESSION_free(3).
+ *copy = 0;
+ return session;
+}
+
+static void
+setSessionCallbacks(SSL_CTX *ctx)
+{
+ if (SslSessionCache) {
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_new_cb(ctx, store_session_cb);
+ SSL_CTX_sess_set_remove_cb(ctx, remove_session_cb);
+ SSL_CTX_sess_set_get_cb(ctx, get_session_cb);
+ }
+}
+
+static bool
+isSslServer()
+{
+ if (Config.Sockaddr.https)
+ return true;
+
+ for (AnyP::PortCfg *s = Config.Sockaddr.http; s; s = s->next) {
+ if (s->flags.tunnelSslBumping)
+ return true;
+ }
+
+ return false;
+}
+
+#define SSL_SESSION_ID_SIZE 32
+#define SSL_SESSION_MAX_SIZE 10*1024
+
+void
+Ssl::initialize_session_cache()
+{
+
+ if (!isSslServer()) //no need to configure ssl session cache.
+ return;
+
+ // Check if the MemMap keys and data are enough big to hold
+ // session ids and session data
+ assert(SSL_SESSION_ID_SIZE >= MEMMAP_SLOT_KEY_SIZE);
+ assert(SSL_SESSION_MAX_SIZE >= MEMMAP_SLOT_DATA_SIZE);
+
+
+ int configuredItems = ::Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot);
+ if (IamWorkerProcess() && configuredItems)
+ SslSessionCache = new Ipc::MemMap(SslSessionCacheName);
+ else {
+ SslSessionCache = NULL;
+ return;
+ }
+
+ for (AnyP::PortCfg *s = ::Config.Sockaddr.https; s; s = s->next) {
+ if (s->staticSslContext.get() != NULL)
+ setSessionCallbacks(s->staticSslContext.get());
+ }
+
+ for (AnyP::PortCfg *s = ::Config.Sockaddr.http; s; s = s->next) {
+ if (s->staticSslContext.get() != NULL)
+ setSessionCallbacks(s->staticSslContext.get());
+ }
+}
+
+void
+destruct_session_cache()
+{
+ delete SslSessionCache;
+}
+
+/// initializes shared memory segments used by MemStore
+class SharedSessionCacheRr: public Ipc::Mem::RegisteredRunner
+{
+public:
+ /* RegisteredRunner API */
+ SharedSessionCacheRr(): owner(NULL) {}
+ virtual void run(const RunnerRegistry &);
+ virtual ~SharedSessionCacheRr();
+
+protected:
+ virtual void create(const RunnerRegistry &);
+
+private:
+ Ipc::MemMap::Owner *owner;
+};
+
+RunnerRegistrationEntry(rrAfterConfig, SharedSessionCacheRr);
+
+void
+SharedSessionCacheRr::run(const RunnerRegistry &r)
+{
+ Ipc::Mem::RegisteredRunner::run(r);
+}
+
+void
+SharedSessionCacheRr::create(const RunnerRegistry &)
+{
+ if (!isSslServer()) //no need to configure ssl session cache.
+ return;
+
+ int items;
+ items = Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot);
+ if (items)
+ owner = Ipc::MemMap::Init(SslSessionCacheName, items);
+}
+
+SharedSessionCacheRr::~SharedSessionCacheRr()
+{
+ delete owner;
+}
+
#endif /* USE_SSL */
=== modified file 'src/ssl/support.h'
--- src/ssl/support.h 2013-07-27 13:37:29 +0000
+++ src/ssl/support.h 2014-01-08 11:01:04 +0000
@@ -260,40 +260,52 @@
*/
bool checkX509ServerValidity(X509 *cert, const char *server);
/**
\ingroup ServerProtocolSSLAPI
* Convert a given ASN1_TIME to a string form.
\param tm the time in ASN1_TIME form
\param buf the buffer to write the output
\param len write at most len bytes
\return The number of bytes written
*/
int asn1timeToString(ASN1_TIME *tm, char *buf, int len);
/**
\ingroup ServerProtocolSSLAPI
* Sets the hostname for the Server Name Indication (SNI) TLS extension
* if supported by the used openssl toolkit.
\return true if SNI set false otherwise
*/
bool setClientSNI(SSL *ssl, const char *fqdn);
+
+/**
+ \ingroup ServerProtocolSSLAPI
+ * Initializes the shared session cache if configured
+*/
+void initialize_session_cache();
+
+/**
+ \ingroup ServerProtocolSSLAPI
+ * Destroy the shared session cache if configured
+*/
+void destruct_session_cache();
} //namespace Ssl
#if _SQUID_WINDOWS_
#if defined(__cplusplus)
/** \cond AUTODOCS-IGNORE */
namespace Squid
{
/** \endcond */
/// \ingroup ServerProtocolSSLAPI
inline
int SSL_set_fd(SSL *ssl, int fd)
{
return ::SSL_set_fd(ssl, _get_osfhandle(fd));
}
/// \ingroup ServerProtocolSSLAPI
#define SSL_set_fd(ssl,fd) Squid::SSL_set_fd(ssl,fd)