函数功能:初始化openssl相关配置,并对每种功能建立索引。
函数功能:创建server级别的ctx、检验证书、密码(cipher)和对ssl->ctx挂共享内存的钩子函数等。
ssl->ctx = SSL_CTX_new(SSLv23_method());
SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, conf);
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1);
函数功能:在merge server的时候调用该函数。
ngx_int_t
ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout)
{
long cache_mode;
//对ctx设置超时
SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
if (ngx_ssl_session_id_context(ssl, sess_ctx) != NGX_OK) {
return NGX_ERROR;
}
。。。
cache_mode = SSL_SESS_CACHE_SERVER;
SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode);
if (shm_zone) {
SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);
SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);
SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
}
return NGX_OK;
}
核心函数:
SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);
SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);
SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);
函数功能:在SSL握手结束后被ngx_ssl_handshake->ssl3_accept->ssl_update_cache中调用。
static int
ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
{
。。。
//计算session的长度
len = i2d_SSL_SESSION(sess, NULL);
/* do not cache too big session */
if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {
return 0;
}
//将session存入buf中
p = buf;
i2d_SSL_SESSION(sess, &p);
c = ngx_ssl_get_connection(ssl_conn);
//得到共享内存地址
ssl_ctx = c->ssl->session_ctx;
shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);
cache = shm_zone->data;
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
ngx_shmtx_lock(&shpool->mutex);
/* drop one or two expired sessions */
ngx_ssl_expire_sessions(cache, shpool, 1);
cached_sess = ngx_slab_alloc_locked(shpool, len);
if (cached_sess == NULL) {
/* drop the oldest non-expired session and try once more */
ngx_ssl_expire_sessions(cache, shpool, 0);
cached_sess = ngx_slab_alloc_locked(shpool, len);
if (cached_sess == NULL) {
sess_id = NULL;
goto failed;
}
}
//key
sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
if (sess_id == NULL) {
}
session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
id = sess_id->sess_id;
ngx_memcpy(cached_sess, buf, len);
ngx_memcpy(id, session_id, session_id_length);
hash = ngx_crc32_short(session_id, session_id_length);
//红黑树存储的节点
sess_id->node.key = hash;
sess_id->node.data = (u_char) session_id_length;
sess_id->id = id;
sess_id->len = len;
sess_id->session = cached_sess;
sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);
ngx_queue_insert_head(&cache->expire_queue, &sess_id->queue);
ngx_rbtree_insert(&cache->session_rbtree, &sess_id->node);
ngx_shmtx_unlock(&shpool->mutex);
return 0;
failed:
if (cached_sess) {
ngx_slab_free_locked(shpool, cached_sess);
}
if (sess_id) {
ngx_slab_free_locked(shpool, sess_id);
}
ngx_shmtx_unlock(&shpool->mutex);
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"could not allocate new session%s", shpool->log_ctx);
return 0;
}
函数功能:在接收到client hello后(即ssl3_get_client_hello)被ngx_ssl_handshake->ssl3_accept->ssl_get_prev_session调用。
static ngx_ssl_session_t *
ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
#if OPENSSL_VERSION_NUMngx_ssl_get_cached_sessionBER >= 0x10100003L
const
#endif
u_char *id, int len, int *copy)
{
。。。
hash = ngx_crc32_short((u_char *) (uintptr_t) id, (size_t) len);
*copy = 0;
c = ngx_ssl_get_connection(ssl_conn);
//得到共享内存地址
shm_zone = SSL_CTX_get_ex_data(c->ssl->session_ctx,
ngx_ssl_session_cache_index);
cache = shm_zone->data;
sess = NULL;
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
ngx_shmtx_lock(&shpool->mutex);
node = cache->session_rbtree.root;
sentinel = cache->session_rbtree.sentinel;
while (node != sentinel) {
if (hash < node->key) {
node = node->left;
continue;
}
if (hash > node->key) {
node = node->right;
continue;
}
/* hash == node->key */
sess_id = (ngx_ssl_sess_id_t *) node;
rc = ngx_memn2cmp((u_char *) (uintptr_t) id, sess_id->id,
(size_t) len, (size_t) node->data);
if (rc == 0) {
if (sess_id->expire > ngx_time()) {
ngx_memcpy(buf, sess_id->session, sess_id->len);
ngx_shmtx_unlock(&shpool->mutex);
p = buf;
sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);
return sess;
}
ngx_queue_remove(&sess_id->queue);
ngx_rbtree_delete(&cache->session_rbtree, node);
ngx_slab_free_locked(shpool, sess_id->session);
#if (NGX_PTR_SIZE == 4)
ngx_slab_free_locked(shpool, sess_id->id);
#endif
ngx_slab_free_locked(shpool, sess_id);
sess = NULL;
goto done;
}
node = (rc < 0) ? node->left : node->right;
}
done:
ngx_shmtx_unlock(&shpool->mutex);
return sess;
}
函数功能:红黑树删除掉session id对应的cache。
(1)浏览器访问#https://slice.b0.upaiyun.com/status
(2)#curl https://slice.b0.upaiyun.com/status
(3)openssl访问
#printf 'GET / HTTP/1.1\r\nhost: slice.b0.upaiyun.com\r\n\r\n' |openssl s_client -connect 127.0.0.1:443 (new_session)
#printf 'GET / HTTP/1.1\r\nhost: slice.b0.upaiyun.com\r\n\r\n' |openssl s_client -connect 127.0.0.1:443 -reconnect (reused_session,会访问server 5次,但server上只看到一次)
#printf 'GET / HTTP/1.1\r\nhost: cjhust.b0.upaiyun.com\r\n\r\n' |openssl s_client -connect 127.0.0.1:443 -sess_in /tmp/session.txt -sess_out /tmp/session.txt -sslv3 (reused_session)
备注:TLS版本会有session ticket扩展,会导致进入不到ngx_ssl_get_cached_sessio,因此参数中加上-sslv3(虽说不安全要淘汰了)。
tengine开发从入门到精通https:
http://tengine.taobao.org/book/chapter_12.html#https
阮一峰对SSL/TLS的研究:
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html
nginx.org SSL模块: