intmain(void){// initializing OpenSSL library
OPENSSL_load_builtin_modules();ENGINE_load_dynamic();// building OpenSSL's configuration file path
charopenssl_cnf_path[]="./openssl.cnf";// loading configuration
if(CONF_modules_load_file(openssl_cnf_path,"openssl_conf",0)!=1){fprintf(stderr,"OpenSSL failed to load required configuration\n");ERR_print_errors_fp(stderr);return1;}ENGINE*eng=ENGINE_by_id("engineX");if(NULL==eng){printf("failed to retrieve engine by id (mppa)\n");return1;}printf("EngineX has been successfully loaded \n");...}
intCONF_module_add(constchar*name,conf_init_func*ifunc,conf_finish_func*ffunc){if(module_add(NULL,name,ifunc,ffunc))return1;elsereturn0;}/* 重要的结构体与全局变量 */staticSTACK_OF(CONF_MODULE)*supported_modules=NULL;staticSTACK_OF(CONF_IMODULE)*initialized_modules=NULL;structconf_module_st{/* DSO of this module or NULL if static */DSO*dso;/* Name of the module */char*name;/* Init function */conf_init_func*init;/* Finish function */conf_finish_func*finish;/* Number of successfully initialized modules */intlinks;void*usr_data;};typedefstructconf_module_stCONF_MODULE;staticCONF_MODULE*module_add(DSO*dso,constchar*name,conf_init_func*ifunc,conf_finish_func*ffunc){CONF_MODULE*tmod=NULL;/* 若supported_modules为空, 则初始化此全局变量,即堆栈的初始化 */if(supported_modules==NULL)supported_modules=sk_CONF_MODULE_new_null();if(supported_modules==NULL)returnNULL;/* 申请配置文件模块结构体conf_module_st的空间 */if((tmod=OPENSSL_zalloc(sizeof(*tmod)))==NULL){CONFerr(CONF_F_MODULE_ADD,ERR_R_MALLOC_FAILURE);returnNULL;}/*
* 此处第一次调用,dso为NULL;
* dso = dynamic shared object, 可以理解为是一个OpenSSL去加载动态库的结构体;
*/tmod->dso=dso;/* 此处记住,将初始化一个叫"engines"的conf_module */tmod->name=OPENSSL_strdup(name);/* 配置文件init函数, 此处即int_engine_module_init。这个函数是关键 */tmod->init=ifunc;/* 配置文件finish函数, 此处即int_engine_module_finish */tmod->finish=ffunc;if(tmod->name==NULL){OPENSSL_free(tmod);returnNULL;}/* 将这个的conf_module结构体入栈进supported_modules这个全局变量栈中 */if(!sk_CONF_MODULE_push(supported_modules,tmod)){OPENSSL_free(tmod->name);OPENSSL_free(tmod);returnNULL;}returntmod;}
\#defineENGINE_load_dynamic() \
OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_DYNAMIC,NULL)intOPENSSL_init_crypto(uint64_topts,constOPENSSL_INIT_SETTINGS*settings){.../*
* RUN_ONCE是多线程时需要关心的,我们这里不关心,就等于调用ossl_init_engine_dynamic
* 最后一波宏展开,调用的是 engine_load_dynamic_int 这个函数
*/if((opts&OPENSSL_INIT_ENGINE_DYNAMIC)&&!RUN_ONCE(&engine_dynamic,ossl_init_engine_dynamic))return0;...}voidengine_load_dynamic_int(void){ENGINE*toadd=engine_dynamic();/* 这命名真是绝了Orz */if(!toadd)return;ENGINE_add(toadd);/*
* If the "add" worked, it gets a structural reference. So either way, we
* release our just-created reference.
*/ENGINE_free(toadd);/*
* If the "add" didn't work, it was probably a conflict because it was
* already added (eg. someone calling ENGINE_load_blah then calling
* ENGINE_load_builtin_engines() perhaps).
*/ERR_clear_error();}
structengine_st{constchar*id;constchar*name;constRSA_METHOD*rsa_meth;constDSA_METHOD*dsa_meth;constDH_METHOD*dh_meth;constEC_KEY_METHOD*ec_meth;constRAND_METHOD*rand_meth;/* Cipher handling is via this callback */ENGINE_CIPHERS_PTRciphers;/* Digest handling is via this callback */ENGINE_DIGESTS_PTRdigests;/* Public key handling via this callback */ENGINE_PKEY_METHS_PTRpkey_meths;/* ASN1 public key handling via this callback */ENGINE_PKEY_ASN1_METHS_PTRpkey_asn1_meths;ENGINE_GEN_INT_FUNC_PTRdestroy;ENGINE_GEN_INT_FUNC_PTRinit;ENGINE_GEN_INT_FUNC_PTRfinish;ENGINE_CTRL_FUNC_PTRctrl;ENGINE_LOAD_KEY_PTRload_privkey;ENGINE_LOAD_KEY_PTRload_pubkey;ENGINE_SSL_CLIENT_CERT_PTRload_ssl_client_cert;constENGINE_CMD_DEFN*cmd_defns;intflags;/* reference count on the structure itself */CRYPTO_REF_COUNTstruct_ref;/*
* reference count on usability of the engine type. NB: This controls the
* loading and initialisation of any functionality required by this
* engine, whereas the previous count is simply to cope with
* (de)allocation of this structure. Hence, running_ref <= struct_ref at
* all times.
*/intfunct_ref;/* A place to store per-ENGINE data */CRYPTO_EX_DATAex_data;/* Used to maintain the linked-list of engines. */structengine_st*prev;structengine_st*next;}
staticconstchar*engine_dynamic_id="dynamic";staticconstchar*engine_dynamic_name="Dynamic engine loading support";staticconstENGINE_CMD_DEFNdynamic_cmd_defns[]={{DYNAMIC_CMD_SO_PATH,"SO_PATH","Specifies the path to the new ENGINE shared library",ENGINE_CMD_FLAG_STRING},{DYNAMIC_CMD_NO_VCHECK,"NO_VCHECK","Specifies to continue even if version checking fails (boolean)",ENGINE_CMD_FLAG_NUMERIC},{DYNAMIC_CMD_ID,"ID","Specifies an ENGINE id name for loading",ENGINE_CMD_FLAG_STRING},{DYNAMIC_CMD_LIST_ADD,"LIST_ADD","Whether to add a loaded ENGINE to the internal list (0=no,1=yes,2=mandatory)",ENGINE_CMD_FLAG_NUMERIC},{DYNAMIC_CMD_DIR_LOAD,"DIR_LOAD","Specifies whether to load from 'DIR_ADD' directories (0=no,1=yes,2=mandatory)",ENGINE_CMD_FLAG_NUMERIC},{DYNAMIC_CMD_DIR_ADD,"DIR_ADD","Adds a directory from which ENGINEs can be loaded",ENGINE_CMD_FLAG_STRING},{DYNAMIC_CMD_LOAD,"LOAD","Load up the ENGINE specified by other settings",ENGINE_CMD_FLAG_NO_INPUT},{0,NULL,NULL,0}};/* 加载动态engine时的命令 */# define ENGINE_FLAGS_BY_ID_COPY (int)0x0004
ENGINEdynamic={.id=engine_dynamic_id,.name=engine_dynamic_name,.init=dynamic_init,/* 空函数,直接return 0 */.finish=dynamic_finish,/* 空函数,直接return 0 */.ctrl=dynamic_ctrl,/* 最重要的函数,后文将分析如何调用到这来 */.flags=ENGINE_FLAGS_BY_ID_COPY,.cmd_defns=dynamic_cmd_defns/*定义了dynamic这个engine ctrl下的合法cmd*/.prev=NULL,.next=NULL/* 说明engine都是以双向链表形式管理 */};
staticENGINE*engine_list_head=NULL;staticENGINE*engine_list_tail=NULL;/* Add another "ENGINE" type into the list. */intENGINE_add(ENGINE*e){intto_return=1;/* 一些入参检查,omit */.../* 全局变量操作时需要加锁以支持多线程 */CRYPTO_THREAD_write_lock(global_engine_lock);/* 核心函数,将刚刚new出来的dynamic加入全局链表中 */if(!engine_list_add(e)){ENGINEerr(ENGINE_F_ENGINE_ADD,ENGINE_R_INTERNAL_LIST_ERROR);to_return=0;}CRYPTO_THREAD_unlock(global_engine_lock);returnto_return;}staticintengine_list_add(ENGINE*e){intconflict=0;ENGINE*iterator=NULL;if(e==NULL){ENGINEerr(ENGINE_F_ENGINE_LIST_ADD,ERR_R_PASSED_NULL_PARAMETER);return0;}/* 从链表头开始迭代 */iterator=engine_list_head;/* 直接遍历到尾部查看有没有重id的情况,重id直接报错退出 */while(iterator&&!conflict){conflict=(strcmp(iterator->id,e->id)==0);iterator=iterator->next;}if(conflict){ENGINEerr(ENGINE_F_ENGINE_LIST_ADD,ENGINE_R_CONFLICTING_ENGINE_ID);return0;}if(engine_list_head==NULL){/* We are adding to an empty list. */if(engine_list_tail){ENGINEerr(ENGINE_F_ENGINE_LIST_ADD,ENGINE_R_INTERNAL_LIST_ERROR);return0;}/* engine_list为空的话则链表头为新建的engine */engine_list_head=e;e->prev=NULL;/*
* The first time the list allocates, we should register the cleanup.
*/engine_cleanup_add_last(engine_list_cleanup);}else{/* We are adding to the tail of an existing list. */if((engine_list_tail==NULL)||(engine_list_tail->next!=NULL)){ENGINEerr(ENGINE_F_ENGINE_LIST_ADD,ENGINE_R_INTERNAL_LIST_ERROR);return0;}/* 将新engine加到队尾的后面 */engine_list_tail->next=e;e->prev=engine_list_tail;}/*
* Having the engine in the list assumes a structural reference.
*/e->struct_ref++;engine_ref_debug(e,0,1);/* 将队尾指向新engine */engine_list_tail=e;e->next=NULL;return1;}
/* 配置文件的method模板 */structconf_method_st{constchar*name;CONF*(*create)(CONF_METHOD*meth);int(*init)(CONF*conf);int(*destroy)(CONF*conf);int(*destroy_data)(CONF*conf);int(*load_bio)(CONF*conf,BIO*bp,long*eline);int(*dump)(constCONF*conf,BIO*bp);int(*is_number)(constCONF*conf,charc);int(*to_int)(constCONF*conf,charc);int(*load)(CONF*conf,constchar*name,long*eline);};/*
* 所有的 AA = BB 都会按照这个格式保存
* 如[openssl_def] engines = engine_section
* 此时这个底下conf_st的哈希表中将保存上一份
* {.section = "openssl_def", .name = "engines", value = "engine_section"}
*/typedefstruct{char*section;char*name;char*value;}CONF_VALUE;structconf_st{CONF_METHOD*meth;/* 动态配置的方法,这里使用default */void*meth_data;LHASH_OF(CONF_VALUE)*data;/* 上文有提到的哈希表 */unsignedintflag_dollarid:1;OPENSSL_CTX*libctx;};/*
* The following section contains the "New CONF" functions. They are
* completely centralised around a new CONF structure that may contain
* basically anything, but at least a method pointer and a table of data.
* These functions are also written in terms of the bridge functions used by
* the "CONF classic" functions, for consistency.
*/CONF*NCONF_new_with_libctx(OPENSSL_CTX*libctx,CONF_METHOD*meth){CONF*ret;if(meth==NULL)meth=NCONF_default();ret=meth->create(meth);if(ret==NULL){CONFerr(0,ERR_R_MALLOC_FAILURE);returnNULL;}/* 这个流程中是NULL,不需要分析 */ret->libctx=libctx;returnret;}
staticintint_engine_configure(constchar*name,constchar*value,constCONF*cnf){.../* 开始对ecmds中栈上的CONF_VALUE遍历,这部分代码都在这个for循环中 */for(i=0;i<sk_CONF_VALUE_num(ecmds);i++){ecmd=sk_CONF_VALUE_value(ecmds,i);/* 解析出ctrlname和ctrlvalue,对应结构体中.name和.value, 下同 */ctrlname=skip_dot(ecmd->name);ctrlvalue=ecmd->value;OSSL_TRACE2(CONF,"ENGINE: doing ctrl(%s,%s)\n",ctrlname,ctrlvalue);/* First handle some special pseudo ctrls *//* Override engine name to use */if(strcmp(ctrlname,"engine_id")==0)/* 把name制成conf文件中engine_id */name=ctrlvalue;...}...}
for(...){...elseif(strcmp(ctrlname,"dynamic_path")==0){/*
* 看到这里是不是豁然开朗,首先找到第二部分初始化的叫做dynamic的engine
* 但这个地方有个值得注意的点,底下分析ENGINE_by_id
*/e=ENGINE_by_id("dynamic");/* 拿到'dynamic'这个ENGINE结构体后,进行三步操作,完成了engineX这个so的加载 *//* 之后我们将单独把ENGINE_ctrl_cmd_string拿出来分析,观察它是如何去加载的*/if(!e)gotoerr;if(!ENGINE_ctrl_cmd_string(e,"SO_PATH",ctrlvalue,0))gotoerr;if(!ENGINE_ctrl_cmd_string(e,"LIST_ADD","2",0))gotoerr;if(!ENGINE_ctrl_cmd_string(e,"LOAD",NULL,0))gotoerr;...}/*
* 完成这三步操作后,'dynamic'副本这个engine已经被重写成了 'engineX'!
* 同时这个engineX也加入了engines的队列中。
*/ENGINE*ENGINE_by_id(constchar*id){/* 入参检查和环境初始化检查 omit */.../* 加锁后开始遍历链表,匹配id = "dynamic" */CRYPTO_THREAD_write_lock(global_engine_lock);iterator=engine_list_head;while(iterator&&(strcmp(id,iterator->id)!=0))iterator=iterator->next;if(iterator!=NULL){/*
* We need to return a structural reference. If this is an ENGINE
* type that returns copies, make a duplicate - otherwise increment
* the existing ENGINE's reference count.
*//* 匹配成功后的小操作:看ENGINE_load_dynamic源码可以看到 dynamic->flag 被设置成了 ENGINE_FLAGS_BY_ID_COPY */if(iterator->flags&ENGINE_FLAGS_BY_ID_COPY){ENGINE*cp=ENGINE_new();if(cp==NULL)iterator=NULL;else{/* 此处很重要! *//* 此处取出的dynamic,不是直接取出链表中的engine节点,而是复制了一个节点 */engine_cpy(cp,iterator);iterator=cp;}}else{iterator->struct_ref++;engine_ref_debug(iterator,0,1);}}CRYPTO_THREAD_unlock(global_engine_lock);if(iterator!=NULL)/* 作为取出返回值,得到了一个dynamic的副本 */returniterator;}
intENGINE_ctrl_cmd_string(ENGINE*e,constchar*cmd_name,constchar*arg,intcmd_optional){intnum,flags;longl;char*ptr;.../* 宏的命名已经暴露了一切,通过cmd_name得到cmd_num */if(e->ctrl==NULL||(num=ENGINE_ctrl(e,ENGINE_CTRL_GET_CMD_FROM_NAME,0,(void*)cmd_name,NULL))<=0){...}...}intENGINE_ctrl(ENGINE*e,intcmd,longi,void*p,void(*f)(void)){.../*
* Intercept any "root-level" commands before trying to hand them on to
* ctrl() handlers.
*/switch(cmd){/* 这部分是通用的ctrl,范围为10 ~ 18, 全部进入int_ctrl_helper */caseENGINE_CTRL_HAS_CTRL_FUNCTION:returnctrl_exists;caseENGINE_CTRL_GET_FIRST_CMD_TYPE:caseENGINE_CTRL_GET_NEXT_CMD_TYPE:caseENGINE_CTRL_GET_CMD_FROM_NAME:caseENGINE_CTRL_GET_NAME_LEN_FROM_CMD:caseENGINE_CTRL_GET_NAME_FROM_CMD:caseENGINE_CTRL_GET_DESC_LEN_FROM_CMD:caseENGINE_CTRL_GET_DESC_FROM_CMD:caseENGINE_CTRL_GET_CMD_FLAGS:/*
* 这里dynamic的flag为ENGINE_FLAGS_BY_ID_COPY,0x0004
* ENGINE_FLAGS_MANUAL_CMD_CTRL = 0x0002,与的结果为0
*/if(ctrl_exists&&!(e->flags&ENGINE_FLAGS_MANUAL_CMD_CTRL))returnint_ctrl_helper(e,cmd,i,p,f);if(!ctrl_exists){ENGINEerr(ENGINE_F_ENGINE_CTRL,ENGINE_R_NO_CONTROL_FUNCTION);/*
* For these cmd-related functions, failure is indicated by a -1
* return value (because 0 is used as a valid return in some
* places).
*/return-1;}default:break;}/* Anything else requires a ctrl() handler to exist. *//* 这里是确定当前engine->ctrl != NULL */if(!ctrl_exists){ENGINEerr(ENGINE_F_ENGINE_CTRL,ENGINE_R_NO_CONTROL_FUNCTION);return0;}/* 调用上面看到的 dynamic->ctrl = dynamic_ctrl, 后面会调用到这来 */returne->ctrl(e,cmd,i,p,f);}/* 这个函数也将反复调用(吐槽下openssl这鬼之设计),我们这里先看当前的cmd */staticintint_ctrl_helper(ENGINE*e,intcmd,longi,void*p,void(*f)(void)){intidx;char*s=(char*)p;constENGINE_CMD_DEFN*cdp;.../* Now handle cmd_name -> cmd_num conversion */if(cmd==ENGINE_CTRL_GET_CMD_FROM_NAME){/* 从dynamic的cmd_defns中去匹配cmd_name,假设是"SO_PATH",
直接去查第二部分的dynamic_cmd_defns,刚好匹配上idx = 0 */if((e->cmd_defns==NULL)||((idx=int_ctrl_cmd_by_name(e->cmd_defns,s))<0)){ENGINEerr(ENGINE_F_INT_CTRL_HELPER,ENGINE_R_INVALID_CMD_NAME);return-1;}/* 查idx = 0时的 cmd_num = 200 = DYNAMIC_CMD_SO_PATH */returne->cmd_defns[idx].cmd_num;}...}
intENGINE_ctrl_cmd_string(ENGINE*e,constchar*cmd_name,constchar*arg,intcmd_optional){/* 继续调用公用ctrl,进入到int_ctrl_helper
(看底下开源的注释,两个函数做的ctrl操作一样的,为啥这么搞也许就是未解之谜吧) */...if(!ENGINE_cmd_is_executable(e,num)){ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,ENGINE_R_CMD_NOT_EXECUTABLE);return0;}/* 顾名思义,拿到dynamic的flag,这里将得到idx = 0时,cmd_defns表中0处的第四个元素 */flags=ENGINE_ctrl(e,ENGINE_CTRL_GET_CMD_FLAGS,num,NULL,NULL);if(flags<0){/*
* Shouldn't happen, given that ENGINE_cmd_is_executable() returned
* success.
*/ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,ENGINE_R_INTERNAL_LIST_ERROR);return0;}}staticintint_ctrl_helper(ENGINE*e,intcmd,longi,void*p,void(*f)(void)){...if((e->cmd_defns==NULL)||((idx=int_ctrl_cmd_by_num(e->cmd_defns,(unsignedint)i))<0)){ENGINEerr(ENGINE_F_INT_CTRL_HELPER,ENGINE_R_INVALID_CMD_NUMBER);return-1;}/* Now the logic splits depending on command type */cdp=&e->cmd_defns[idx];switch(cmd){...caseENGINE_CTRL_GET_CMD_FLAGS:/* 可以查出来上面的是 ENGINE_CMD_FLAG_STRING = 0x0002 */returncdp->cmd_flags;}...}
intENGINE_ctrl_cmd_string(ENGINE*e,constchar*cmd_name,constchar*arg,intcmd_optional){.../* ENGINE_CMD_FLAG_NO_INPUT = 0x0004 */if(flags&ENGINE_CMD_FLAG_NO_INPUT){/* 如果命令查出来的flag应该没有arg_input, 但arg非空,直接退出???? */if(arg!=NULL){ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,ENGINE_R_COMMAND_TAKES_NO_INPUT);return0;}/*
* We deliberately force the result of ENGINE_ctrl() to 0 or 1 rather
* than returning it as "return data". This is to ensure usage of
* these commands is consistent across applications and that certain
* applications don't understand it one way, and others another.
*//* 最后"LOAD"命令走的这 */if(ENGINE_ctrl(e,num,0,(void*)arg,NULL)>0)return1;return0;}/* So, we require input */if(arg==NULL){ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,ENGINE_R_COMMAND_TAKES_INPUT);return0;}/* 一定有更好的写法吧,这种判断也太迷惑了。。 *//* If it takes string input, that's easy */if(flags&ENGINE_CMD_FLAG_STRING){/* Same explanation as above *//* 所以应该调用到这,注意此时num 将大于200, 肯定不是默认的流程,
这就走到了return e->ctrl(e, cmd, i, p, f); 即 dynamic_ctrl */if(ENGINE_ctrl(e,num,0,(void*)arg,NULL)>0)return1;return0;}/* 此时arg是数字,需要从str转int,LIST_ADD走这 */if(!(flags&ENGINE_CMD_FLAG_NUMERIC)){ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,ENGINE_R_INTERNAL_LIST_ERROR);return0;}l=strtol(arg,&ptr,10);if((arg==ptr)||(*ptr!='\0')){ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER);return0;}/*
* Force the result of the control command to 0 or 1, for the reasons
* mentioned before.
*/if(ENGINE_ctrl(e,num,l,NULL,NULL)>0)return1;...}
/* 动态库加载的上下文 */structst_dynamic_data_ctx{/* The DSO object we load that supplies the ENGINE code */DSO*dynamic_dso;/*
* The function pointer to the version checking shared library function
*/dynamic_v_check_fnv_check;/*
* The function pointer to the engine-binding shared library function
*/dynamic_bind_enginebind_engine;/* The default name/path for loading the shared library */char*DYNAMIC_LIBNAME;/* Whether to continue loading on a version check failure */intno_vcheck;/* If non-NULL, stipulates the 'id' of the ENGINE to be loaded */char*engine_id;/*
* If non-zero, a successfully loaded ENGINE should be added to the
* internal ENGINE list. If 2, the add must succeed or the entire load
* should fail.
*/intlist_add_value;/* The symbol name for the version checking function */constchar*DYNAMIC_F1;/* The symbol name for the "initialise ENGINE structure" function */constchar*DYNAMIC_F2;/*
* Whether to never use 'dirs', use 'dirs' as a fallback, or only use
* 'dirs' for loading. Default is to use 'dirs' as a fallback.
*/intdir_load;/* A stack of directories from which ENGINEs could be loaded */STACK_OF(OPENSSL_STRING)*dirs;};staticintdynamic_ctrl(ENGINE*e,intcmd,longi,void*p,void(*f)(void)){/* 这个函数将会初始化并保存动态库数据的ctx,这也是为什么可以反复调用这个接口的原因 */dynamic_data_ctx*ctx=dynamic_get_data_ctx(e);intinitialised;if(!ctx){ENGINEerr(ENGINE_F_DYNAMIC_CTRL,ENGINE_R_NOT_LOADED);return0;}/* 可以看到,加载完成的标志是dynamic_dso钩子已经挂上了 */initialised=((ctx->dynamic_dso==NULL)?0:1);/* All our control commands require the ENGINE to be uninitialised */if(initialised){ENGINEerr(ENGINE_F_DYNAMIC_CTRL,ENGINE_R_ALREADY_LOADED);return0;}/* cmd解析,底下逐个分析 */...}/*
* This function retrieves the context structure from an ENGINE's "ex_data",
* or if it doesn't exist yet, sets it up.
*/staticdynamic_data_ctx*dynamic_get_data_ctx(ENGINE*e){dynamic_data_ctx*ctx;if(dynamic_ex_data_idx<0){/*
* Create and register the ENGINE ex_data, and associate our "free"
* function with it to ensure any allocated contexts get freed when
* an ENGINE goes underground.
*/intnew_idx=ENGINE_get_ex_new_index(0,NULL,NULL,NULL,dynamic_data_ctx_free_func);if(new_idx==-1){ENGINEerr(ENGINE_F_DYNAMIC_GET_DATA_CTX,ENGINE_R_NO_INDEX);returnNULL;}CRYPTO_THREAD_write_lock(global_engine_lock);/* Avoid a race by checking again inside this lock */if(dynamic_ex_data_idx<0){/* Good, someone didn't beat us to it */dynamic_ex_data_idx=new_idx;new_idx=-1;}CRYPTO_THREAD_unlock(global_engine_lock);/*
* In theory we could "give back" the index here if (new_idx>-1), but
* it's not possible and wouldn't gain us much if it were.
*/}ctx=(dynamic_data_ctx*)ENGINE_get_ex_data(e,dynamic_ex_data_idx);/* Check if the context needs to be created */if((ctx==NULL)&&!dynamic_set_data_ctx(e,&ctx))/* "set_data" will set errors if necessary */returnNULL;returnctx;}/*
* 简单的说就是去查挂在engine->ex_data,
* 这个就是动态库加载的上下文,ex_data是个栈可能有多个上下文,
* 根据一个全局变量dynamic_ex_data_idx确定当前使用上下文
* 当然第一次调用ctx是空的,所以需要调用一下dynamic_set_data_ctx初始化
*/staticintdynamic_set_data_ctx(ENGINE*e,dynamic_data_ctx**ctx){/* 申请ctx的mem */dynamic_data_ctx*c=OPENSSL_zalloc(sizeof(*c));intret=1;if(c==NULL){ENGINEerr(ENGINE_F_DYNAMIC_SET_DATA_CTX,ERR_R_MALLOC_FAILURE);return0;}c->dirs=sk_OPENSSL_STRING_new_null();if(c->dirs==NULL){ENGINEerr(ENGINE_F_DYNAMIC_SET_DATA_CTX,ERR_R_MALLOC_FAILURE);OPENSSL_free(c);return0;}/* 初始化一些字段,下面总结 */c->DYNAMIC_F1="v_check",;c->DYNAMIC_F2="bind_engine";c->dir_load=1;CRYPTO_THREAD_write_lock(global_engine_lock);/* 第一次进来为NULL(然而正常是为ctx = NULL才会调用这个函数,可能是冗余校验)*/if((*ctx=(dynamic_data_ctx*)ENGINE_get_ex_data(e,dynamic_ex_data_idx))==NULL){/* Good, we're the first *//* 把ctx挂在engine->ex_data上 */ret=ENGINE_set_ex_data(e,dynamic_ex_data_idx,c);if(ret){*ctx=c;c=NULL;}}CRYPTO_THREAD_unlock(global_engine_lock);/*
* If we lost the race to set the context, c is non-NULL and *ctx is the
* context of the thread that won.
*/if(c)sk_OPENSSL_STRING_free(c->dirs);OPENSSL_free(c);returnret;}/*
* 得到最后的结果 dynamic->ex_data = ctx;
* ctx = {.DYNAMIC_F1 = "v_check", .DYNAMIC_F2 = "bind_engine", c->dir_load = 1}
* 惊奇的发现了 bind_engine 虽然他只是个字符串,但是我相信你已经知道原因了
* 他需要在动态库中去寻找这个符号
*/
staticintdynamic_ctrl(ENGINE*e,intcmd,longi,void*p,void(*f)(void)){...switch(cmd){/* 注意, p就是ctrlvalue,即从conf中取下来的值 */caseDYNAMIC_CMD_SO_PATH:/* a NULL 'p' or a string of zero-length is the same thing */if(p&&(strlen((constchar*)p)<1))p=NULL;OPENSSL_free(ctx->DYNAMIC_LIBNAME);if(p)/* 很明显只是做了个简单的复制,此时路径已经赋值上了 */ctx->DYNAMIC_LIBNAME=OPENSSL_strdup(p);elsectx->DYNAMIC_LIBNAME=NULL;return(ctx->DYNAMIC_LIBNAME?1:0);caseDYNAMIC_CMD_LIST_ADD:if((i<0)||(i>2)){ENGINEerr(ENGINE_F_DYNAMIC_CTRL,ENGINE_R_INVALID_ARGUMENT);return0;}/* 很简单,赋值而已 */ctx->list_add_value=(int)i;return1;...}}
staticintdynamic_ctrl(ENGINE*e,intcmd,longi,void*p,void(*f)(void)){...switch(cmd){caseDYNAMIC_CMD_LOAD:returndynamic_load(e,ctx);...}}staticintdynamic_load(ENGINE*e,dynamic_data_ctx*ctx){ENGINEcpy;dynamic_fnsfns;/*
* 先new一个DSO结构体,DSO这一套函数怎么玩的这里先不讲了,
* 可以理解为内部也有一个加载钩子,有4个挂载点,估计再展开讲读者疯了
*/if(ctx->dynamic_dso==NULL)ctx->dynamic_dso=DSO_new();if(ctx->dynamic_dso==NULL)return0;/* 此处检查DYNAMIC_LIBNAME不能为空,这个就是dso的加载地址 */if(!ctx->DYNAMIC_LIBNAME){if(!ctx->engine_id)return0;DSO_ctrl(ctx->dynamic_dso,DSO_CTRL_SET_FLAGS,DSO_FLAG_NAME_TRANSLATION_EXT_ONLY,NULL);ctx->DYNAMIC_LIBNAME=DSO_convert_filename(ctx->dynamic_dso,ctx->engine_id);}/* 核心加载函数int_load,看下面分析 */if(!int_load(ctx)){ENGINEerr(ENGINE_F_DYNAMIC_LOAD,ENGINE_R_DSO_NOT_FOUND);DSO_free(ctx->dynamic_dso);ctx->dynamic_dso=NULL;return0;}/* We have to find a bind function otherwise it'll always end badly *//*
* 此时engine动态库已经加载如内存,符号表与对应地址也准备完成
* 所以肯定是需要去寻找这个绑定engine完成加载的函数了,胜利的曙光
* DSO_bind_func会在符号表中去匹配第二个参数字符串,这里就是我们要的"bind_engine"
* 并返回上它的函数地址,挂载在ctx->bind_engine上
*/if(!(ctx->bind_engine=(dynamic_bind_engine)DSO_bind_func(ctx->dynamic_dso,ctx->DYNAMIC_F2))){ctx->bind_engine=NULL;DSO_free(ctx->dynamic_dso);ctx->dynamic_dso=NULL;ENGINEerr(ENGINE_F_DYNAMIC_LOAD,ENGINE_R_DSO_FAILURE);return0;}/* Do we perform version checking? */if(!ctx->no_vcheck){unsignedlongvcheck_res=0;/*
* Now we try to find a version checking function and decide how to
* cope with failure if/when it fails.
*/ctx->v_check=(dynamic_v_check_fn)DSO_bind_func(ctx->dynamic_dso,ctx->DYNAMIC_F1);if(ctx->v_check)vcheck_res=ctx->v_check(OSSL_DYNAMIC_VERSION);/*
* We fail if the version checker veto'd the load *or* if it is
* deferring to us (by returning its version) and we think it is too
* old.
*/if(vcheck_res<OSSL_DYNAMIC_OLDEST){/* Fail */ctx->bind_engine=NULL;ctx->v_check=NULL;DSO_free(ctx->dynamic_dso);ctx->dynamic_dso=NULL;ENGINEerr(ENGINE_F_DYNAMIC_LOAD,ENGINE_R_VERSION_INCOMPATIBILITY);return0;}}/*
* First binary copy the ENGINE structure so that we can roll back if the
* hand-over fails
*/memcpy(&cpy,e,sizeof(ENGINE));/*
* Provide the ERR, "ex_data", memory, and locking callbacks so the
* loaded library uses our state rather than its own. FIXME: As noted in
* engine.h, much of this would be simplified if each area of code
* provided its own "summary" structure of all related callbacks. It
* would also increase opaqueness.
*/fns.static_state=ENGINE_get_static_state();CRYPTO_get_mem_functions(&fns.mem_fns.malloc_fn,&fns.mem_fns.realloc_fn,&fns.mem_fns.free_fn);/*
* Now that we've loaded the dynamic engine, make sure no "dynamic"
* ENGINE elements will show through.
*/engine_set_all_null(e);/* Try to bind the ENGINE onto our own ENGINE structure *//* !!!!Attension, 终于调用成功了,我们的engineX终于被设置好了! */if(!ctx->bind_engine(e,ctx->engine_id,&fns)){ctx->bind_engine=NULL;ctx->v_check=NULL;DSO_free(ctx->dynamic_dso);ctx->dynamic_dso=NULL;ENGINEerr(ENGINE_F_DYNAMIC_LOAD,ENGINE_R_INIT_FAILED);/* Copy the original ENGINE structure back */memcpy(e,&cpy,sizeof(ENGINE));return0;}/* Do we try to add this ENGINE to the internal list too? *//* 把这个engine的副本add进上面engine全局链表,大功告成!*/if(ctx->list_add_value>0){if(!ENGINE_add(e)){/* Do we tolerate this or fail? */if(ctx->list_add_value>1){/*
* Fail - NB: By this time, it's too late to rollback, and
* trying to do so allows the bind_engine() code to have
* created leaks. We just have to fail where we are, after
* the ENGINE has changed.
*/ENGINEerr(ENGINE_F_DYNAMIC_LOAD,ENGINE_R_CONFLICTING_ENGINE_ID);return0;}/* Tolerate */ERR_clear_error();}}return1;}staticintint_load(dynamic_data_ctx*ctx){intnum,loop;/* Unless told not to, try a direct load *//*
* DSO_load去打开ctx->DYNAMIC_LIBNAME,把egine对应的lib库加载进内存
* 解析符号表和对应地址到上面申请好的ctx->dynamic_dso结构体中
*/if((ctx->dir_load!=2)&&(DSO_load(ctx->dynamic_dso,ctx->DYNAMIC_LIBNAME,NULL,0))!=NULL)return1;/* If we're not allowed to use 'dirs' or we have none, fail */if(!ctx->dir_load||(num=sk_OPENSSL_STRING_num(ctx->dirs))<1)return0;for(loop=0;loop<num;loop++){/* 还有链接的dso这里会处理递归的去加载,对应的需要在ctx->dirs中 */constchar*s=sk_OPENSSL_STRING_value(ctx->dirs,loop);char*merge=DSO_merge(ctx->dynamic_dso,ctx->DYNAMIC_LIBNAME,s);if(!merge)return0;if(DSO_load(ctx->dynamic_dso,merge,NULL,0)){/* Found what we're looking for */OPENSSL_free(merge);return1;}OPENSSL_free(merge);}return0;}