完成ssl session cache部分的编写。

This commit is contained in:
zhengchao
2018-08-24 18:38:17 +08:00
parent 0f87411d01
commit 6d7940ff00
2 changed files with 314 additions and 0 deletions

View File

@@ -0,0 +1,299 @@
#include <assert.h>
#include <ssl_sess_cache.h>
#include <ssl.h>
#include <MESA_htable.h>
#include <field_stat2.h>
#define SESS_CACHE_NOT_FOUND -1
#define SESS_CACHE_FOUND 0
#define SESS_CACHE_UPDATE_OLD 1
#define SESS_CACHE_ADD_NEW 2
#define SESS_CACHE_INVALID 3
struct asn1_sess
{
unsigned char* buff;
size_t size;
};
struct sess_set_args
{
MESA_htable_handle hash;
struct asn1_sess* new_sess;
};
struct sess_cache
{
enum tfe_conn_dir served_for;
MESA_htable_handle hash;
long long hit_cnt, miss_cnt,del_err;
};
static void ssl_sess_free_serialized(void *data)
{
struct asn1_sess* p=(struct asn1_sess*)data;
free(p->buff);
p->size=0;
free(p);
return;
}
static struct asn1_sess* ssl_sess_serialize(SSL_SESSION *sess)
{
struct asn1_sess* result=ALLOC(struct asn1_sess,1);
result->size = i2d_SSL_SESSION(sess, NULL);
/*When using i2d_SSL_SESSION(), the memory location pointed to by pp must be large enough to hold the binary representation of the session.
There is no known limit on the size of the created ASN1 representation, so the necessary amount of space should be obtained by first calling
i2d_SSL_SESSION() with pp=NULL, and obtain the size needed, then allocate the memory and call i2d_SSL_SESSION() again.*/
result->buff=ALLOC(unsigned char,result->size);
i2d_SSL_SESSION(sess, &(result->buff));
return result;
}
static SSL_SESSION * ssl_sess_deserialize(const struct asn1_sess* asn1)
{
SSL_SESSION *sess=NULL;
sess = d2i_SSL_SESSION(NULL, &(asn1->buff), asn1->size); /* increments asn1 */
return sess;
}
static int ssl_sess_varify_cb(void *data, int eliminate_type)
{
SSL_SESSION *sess=NULL;
int ret=0;
const struct asn1_sess* asn1=(struct asn1_sess*)data;
if(eliminate_type==ELIMINATE_TYPE_NUM)
{
return 1; //direct expired.
}
sess=ssl_sess_deserialize(asn1);
ret=ssl_session_is_valid(sess);
SSL_SESSION_free(sess);
if(ret==0)
{
return 1; //should be expired (deleted).
}
else
{
return 0;
}
}
static long sess_cache_get_cb(void *data, const uchar *key, uint size, void *user_arg)
{
SSL_SESSION *sess=NULL;
int is_valid=0;
if(data==NULL)
{
return SESS_CACHE_NOT_FOUND;
}
const struct asn1_sess* asn1=(struct asn1_sess*)data;
sess=ssl_sess_deserialize(data,asn1);
is_valid=ssl_session_is_valid(sess);
if(is_valid==0)
{
SSL_SESSION_free(sess);
return SESS_CACHE_INVALID;
}
else
{
*(SSL_SESSION **)user_arg=sess;
return SESS_CACHE_FOUND;
}
}
static long sess_cache_set_cb(void *data, const uchar *key, uint size, void *user_arg)
{
struct sess_set_args* args=(struct sess_set_args*)user_arg;
struct asn1_sess* new_asn1=args->new_sess;
struct asn1_sess *cur_asn1=(struct asn1_sess*)data;
int ret=0;
if(cur_asn1!=NULL)
{
free(cur_asn1->buff);
cur_asn1->size=new_asn1->size;
cur_asn1->buff=ALLOC(unsigned char, cur_asn1->size);
memcpy(cur_asn1->buff,new_asn1->buff,cur_asn1->size);
return SESS_CACHE_UPDATE_OLD;
}
else
{
ret=MESA_htable_add(args->hash, key, size, new_asn1);
assert(ret>=0);
return SESS_CACHE_ADD_NEW;
}
}
static int upsess_mk_key(struct sockaddr * addr, socklen_t addr_len, const char* sni, unsigned char** key_buf)
{
size_t key_size=0;
unsigned char* tmp=NULL, p=NULL;
size_t tmp_size;
dynbuf_t tmp, *db;
short port;
size_t snilen;
switch (((struct sockaddr_storage *)addr)->ss_family) {
case AF_INET:
tmp = (unsigned char *)
&((struct sockaddr_in*)addr)->sin_addr;
tmp_size = sizeof(struct in_addr);
port = ((struct sockaddr_in*)addr)->sin_port;
break;
case AF_INET6:
tmp = (unsigned char *)
&((struct sockaddr_in6*)addr)->sin6_addr;
tmp_size = sizeof(struct in6_addr);
port = ((struct sockaddr_in6*)addr)->sin6_port;
break;
default:
//should never happens.
assert(0);
break;
}
snilen = sni ? strlen(sni) : 0;
key_size=tmp_size+sizeof(port)+snilen;
*key_buf=ALLOC(unsigned char, key_size);
p=*key_buff;
memcpy(p,tmp,tmp_size);
p+=tmp_size;
memcpy(p, (char*)&port, sizeof(port));
p+=sizeof(port);
return key_size;
}
void up_session_set(struct sess_cache* cache, struct sockaddr * addr, socklen_t addr_len, const char* sni, SSL_SESSION * sess)
{
char* key=NULL;
int ret=0;
size_t key_size=0;
long cb_ret=0;
void* no_use=NULL;
assert(cache->served_for==CONN_DIR_UPSTREAM);
key_size=upsess_mk_key(addr, addr_len, sni, &key);
struct asn1_sess* asn1=NULL;
asn1=ssl_sess_serialize(sess);
struct sess_set_args set_args;
set_args.hash=cache->hash;
set_args.new_sess=asn1;
no_use=MESA_htable_search_cb(cache->hash, key, key_size, sess_cache_set_cb, &set_args,&cb_ret);
if(cb_ret==SESS_CACHE_UPDATE_OLD)
{
ssl_sess_free_serialized(asn1);
}
free(key);
return;
}
SSL_SESSION* up_session_get(struct sess_cache* cache, struct sockaddr * addr, socklen_t addr_len, const char* sni)
{
SSL_SESSION* sess=NULL;
void* no_use=NULL;
long cb_ret=0;
char* key=NULL;
size_t key_size=0;
assert(cache->served_for==CONN_DIR_UPSTREAM);
key_size=upsess_mk_key(addr, addr_len, sni, &key);
no_use=MESA_htable_search_cb(cache->hash, key, key_size,sess_cache_get_cb, &sess, &cb_ret);
free(key);
key=NULL;
if(cb_ret==1)
{
cache->hit_cnt++;
return sess;
}
else
{
cache->miss_cnt++;
return NULL;
}
}
void down_session_set(struct sess_cache* cache, const SSL_SESSION* sess)
{
unsigned int idlen=0;
struct asn1_sess* asn1=NULL;
long cb_ret=0;
void* no_use=NULL;
int ret=0;
assert(cache->served_for==CONN_DIR_DOWNSTREAM);
asn1=ssl_sess_serialize(sess);
/*
* SSL_SESSION_get_id() returns a pointer to the internal session id value for the session s.
* The length of the id in bytes is stored in *idlen. The length may be 0.
* The caller should not free the returned pointer directly.
*/
const unsigned char* id = SSL_SESSION_get_id(sess, &idlen);
struct sess_set_args set_args;
set_args.hash=cache->hash;
set_args.new_sess=asn1;
no_use=MESA_htable_search_cb(cache->hash, id, (unsigned int)idlen, sess_cache_set_cb, &set_args,&cb_ret);
if(cb_ret==SESS_CACHE_UPDATE_OLD)
{
ssl_sess_free_serialized(asn1);
}
return;
}
SSL_SESSION* down_session_get(struct sess_cache* cache, unsigned char * id, int idlen)
{
SSL_SESSION* sess=NULL;
void* no_use=NULL;
long cb_ret=0;
assert(cache->served_for==CONN_DIR_DOWNSTREAM);
no_use=MESA_htable_search_cb(cache->hash, id, (unsigned int)idlen, sess_cache_get_cb, &sess,&cb_ret);
if(cb_ret==1)
{
cache->hit_cnt++;
return sess;
}
else
{
cache->miss_cnt++;
return NULL;
}
}
void down_session_del(struct sess_cache* cache, const SSL_SESSION* sess)
{
assert(cache->served_for==CONN_DIR_DOWNSTREAM);
unsigned int len=0;
const unsigned char* id = SSL_SESSION_get_id(sess, &len);
int ret=MESA_htable_del(cache->hash, id, len, NULL);
if(ret!=MESA_HTABLE_RET_OK)
{
cache->del_err++;
}
return;
}
struct sess_cache* ssl_sess_cache_create(int slot_size, int expire_seconds, enum tfe_conn_dir served)
{
struct sess_cache* cache=ALLOC(struct sess_cache, 1);
MESA_htable_handle htable=NULL;
int ret=0,max_num=slot_size*4;
htable=MESA_htable_born();
value=0;//no print
ret=MESA_htable_set_opt(htable, MHO_SCREEN_PRINT_CTRL, &(value), sizeof(value));
value=1;//thread safe
ret=MESA_htable_set_opt(htable, MHO_THREAD_SAFE, value, sizeof(value));
assert(ret==0);
value=16;
ret=MESA_htable_set_opt(htable, MHO_MUTEX_NUM, value, sizeof(value));
ret=MESA_htable_set_opt(htable, MHO_HASH_SLOT_SIZE, &(slot_size), sizeof(slot_size));
ret=MESA_htable_set_opt(htable, MHO_HASH_MAX_ELEMENT_NUM, &(max_num), sizeof(max_num));
ret=MESA_htable_set_opt(htable, MHO_EXPIRE_TIME, &(expire_seconds), sizeof(expire_seconds));
value=HASH_ELIMINATE_ALGO_FIFO;
ret=MESA_htable_set_opt(htable, MHO_ELIMIMINATE_TYPE, &(value), sizeof(value));
ret=MESA_htable_set_opt(htable, MHO_CBFUN_DATA_FREE, ssl_sess_free_serialized, sizeof(ssl_sess_free_serialized));
ret=MESA_htable_set_opt(htable, MHO_CBFUN_DATA_EXPIRE_NOTIFY, ssl_sess_varify_cb, sizeof(ssl_sess_varify_cb));
assert(ret==0);
ret=MESA_htable_mature(htable);
assert(ret==0);
cache->hash=htable;
cache->served_for=served;
return cache;
}
void ssl_sess_cache_destroy(struct sess_cache* cache)
{
MESA_htable_destroy(cache->hash, NULL);
cache->hash=NULL;
free(cache);
return;
}