diff --git a/platform/src/ssl_sess_cache.cpp b/platform/src/ssl_sess_cache.cpp new file mode 100644 index 0000000..3f05093 --- /dev/null +++ b/platform/src/ssl_sess_cache.cpp @@ -0,0 +1,299 @@ +#include + +#include +#include +#include +#include + +#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; +} + diff --git a/platform/src/ssl_sess_cache.h b/platform/src/ssl_sess_cache.h new file mode 100644 index 0000000..ece9db0 --- /dev/null +++ b/platform/src/ssl_sess_cache.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +#include + +struct sess_cache; +struct sess_cache* ssl_sess_cache_create(int slot_size, int expire_seconds, enum tfe_conn_dir served); +void ssl_sess_cache_destroy(struct sess_cache* cache); + +void up_session_set(struct sess_cache* cache, struct sockaddr * addr, socklen_t addr_len, const char* sni, SSL_SESSION * value) +SSL_SESSION* up_session_get(struct sess_cache* cache, struct sockaddr *addr, socklen_t addr_len, const char* sni); +void down_session_set(struct sess_cache* cache, const SSL_SESSION* sess); +SSL_SESSION* down_session_get(struct sess_cache* cache, unsigned char * id, int idlen); +