nginx调用openssl函数源码分析_istan1ey的博客-程序员宅基地

技术标签: 网络  nginx  nginx开发学习汇总  网络协议  ssl  nginx反向代理  

NGINX部分

在ngx_http_init_connection中把recv→handler设置为ngx_http_ssl_handshake。

然后将这个读时间加入到epoll中,主要是分析handshake函数。

receive client hello

static void

ngx_http_ssl_handshake(ngx_event_t *rev)

{

...

    n = recv(c->fd, (char *) buf, size, MSG_PEEK);

    //判断协议

    if (n == 1) {

        if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {

            // 获取loc conf和server conf
            clcf = ngx_http_get_module_loc_conf(hc->conf_ctx,
                                                ngx_http_core_module);


            if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
                ngx_http_close_connection(c);
                return;
            }


            sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
                                                ngx_http_ssl_module);

            // 调用该函数生成ssl

            if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER  != NGX_OK)
            {
                ngx_http_close_connection(c);
                return;
            }
        }

    }
...

}

ngx_ssl_create_connection

ngx_int_t

ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)

{

...

    sc->session_ctx = ssl->ctx;

    sc->connection = SSL_new(ssl->ctx);


    if (sc->connection == NULL) {

        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed");

        return NGX_ERROR;

    }

    if (SSL_set_fd(sc->connection, c->fd) == 0) {

        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed");

        return NGX_ERROR;

    }

...

}

first收到client hello之后,完成初始化。

然后调用ngx_ssl_handshake函数,里面会调用openssl的ssl_do_handshake。

ngx_int_t

ngx_ssl_handshake(ngx_connection_t *c)

{

...

    n = ngx_ssl_handshake_early_data(c);

    n = SSL_do_handshake(c->ssl->connection);

...

}

do handshake的时候调用的是openssl的async job的库

OPENSSL部分

ASYNC JOB

int SSL_do_handshake(SSL *s)

{

...

if (SSL_in_init(s) || SSL_in_before(s)) {

        if ((s->mode & SSL_MODE_ASYNC) && ASYNC_get_current_job() == NULL) {

            struct ssl_async_args args;

            args.s = s;

            ret = ssl_start_async_job(s, &args, ssl_do_handshake_intern);

        } else {

            ret = s->handshake_func(s);

        }

    }

...

}
int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *wctx, int *ret,
                    int (*func)(void *), void *args, size_t size)

{

...
    /* Start a new job */
    if ((ctx->currjob = async_get_pool_job()) == NULL)
        return ASYNC_NO_JOBS;
...

}

static ASYNC_JOB *async_get_pool_job(void) {
...
if (job == NULL) {

        /* Pool is empty */

        if ((pool->max_size != 0) && (pool->curr_size >= pool->max_size))

            return NULL;

        job = async_job_new();

        if (job != NULL) {
            if (! async_fibre_makecontext(&job->fibrectx)) {
                async_job_free(job);
                return NULL;
            }
            pool->curr_size++;
        }
    }
...
}

先get context做初始化,然后malloc一个stack,创建堆栈把函数放进去。

makecontext创建调用运行函数async_start_func,函数本身是使用当前job中的func。

int async_fibre_makecontext(async_fibre *fibre)

{

    fibre->env_init = 0;

    if (getcontext(&fibre->fibre) == 0) {           //初始化当前ucontext

        fibre->fibre.uc_stack.ss_sp = OPENSSL_malloc(STACKSIZE);

        if (fibre->fibre.uc_stack.ss_sp != NULL) {

            fibre->fibre.uc_stack.ss_size = STACKSIZE;

            fibre->fibre.uc_link = NULL;

            makecontext(&fibre->fibre, async_start_func, 0);

            return 1;

        }

    } else {

        fibre->fibre.uc_stack.ss_sp = NULL;

    }

    return 0;

}

pause job最关键的是swapcontext,在func中一旦被调用的话,就可以立即切换栈信息。

切回start_job的主函数,根据job→status=ASYNC_JOB_PAUSING来返回

int ASYNC_pause_job(void)

{

...
    if (!async_fibre_swapcontext(&job->fibrectx,
                                 &ctx->dispatcher, 1)) {

        ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);

        return 0;

    }
...

}

切回主函数后可以发现start job是for死循环任务,会根据job的状态进行返回

int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *wctx, int *ret,

                    int (*func)(void *), void *args, size_t size)

{

...

    for (;;) {

        if (ctx->currjob != NULL) {

            if (ctx->currjob->status == ASYNC_JOB_PAUSING) {

                *job = ctx->currjob;

                ctx->currjob->status = ASYNC_JOB_PAUSED;

                ctx->currjob = NULL;

                return ASYNC_PAUSE;

            }

            if (ctx->currjob->status == ASYNC_JOB_PAUSED) {

                ctx->currjob = *job;

                /* Resume previous job */

                if (!async_fibre_swapcontext(&ctx->dispatcher,

                        &ctx->currjob->fibrectx, 1)) {

                    ASYNCerr(ASYNC_F_ASYNC_START_JOB,

                             ASYNC_R_FAILED_TO_SWAP_CONTEXT);

                    goto err;

                }

                continue;

            }
...

}
static int ssl_start_async_job(SSL *s, struct ssl_async_args *args,

                               int (*func) (void *))

{

...

    switch (ASYNC_start_job(&s->job, s->waitctx, &ret, func, args,
                            sizeof(struct ssl_async_args))) {

    case ASYNC_ERR:

        s->rwstate = SSL_NOTHING;

        SSLerr(SSL_F_SSL_START_ASYNC_JOB, SSL_R_FAILED_TO_INIT_ASYNC);

        return -1;

    case ASYNC_PAUSE:

        s->rwstate = SSL_ASYNC_PAUSED;

        return -1;

}

...

}

返回的这个状态码,在nginx里面接到就是SSL_ERROR_WANT_ASYNC。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/realmardrid/article/details/122654229

智能推荐

TypeError: read_excel() got an unexpected keyword argument ‘encoding‘_read_excel encoding_Archilleuser的博客-程序员宅基地

eclipse TypeError: read_excel() got an unexpected keyword argument ‘encoding’代码中有 enconding=“utf8”, 但是在最新的 pandas read_excel()函数中没有关于编码的规定,所以会报typeerror。 我自己的做法是直接删除 enconding=“utf8”。如果是其他的编码格式,我自己也不大清楚怎么办了。以后弄了我再来补吧。当然也欢迎大佬赐教..._read_excel encoding

【简介】成鹏致远·得天下·安知己_成鹏致远的博客-程序员宅基地

成鹏致远 不了解成鹏致远? 没关系!慢慢往下看! 得知己者,得天下;得天下,安知己!650) this.width=650;" src="http://img1.51cto.com/attachment/201306/150355372.gif" title="成鹏致远,相信自己.gif" />650) this.width=650;" src="http://img1.51ct

极简404页面模板|纯HTML【少量css】_红袍法师的博客-程序员宅基地

记着点赞<!DOCTYPE html><html style="height:100%"><head> <meta name="viewport" charset="utf-8" content="width=device-width, initial-scale=1, shrink-to-fit=no" > <tit..._极简404页面模板

bootstrap table设置列宽_bootstrap table 列宽_芃蓉蓁蓁的博客-程序员宅基地

列表宽度设置前:第一步:需要设置css的table-layout属性值为fixed,默认auto,<style type="text/css"> .table {table-layout:fixed;}</style>table-layout可能值描述auto默认。列宽度由单元格内容设定。fixed列宽由表格宽度和列宽度设定。inherit规定应该从父元素继承 table-layout 属性的值。第二步:在每一列设置w_bootstrap table 列宽

Hudi社区 | 致广大数据湖用户的一封信_hudi 论坛_大数据技术架构的博客-程序员宅基地

随着数据湖概念的流行,涌现了很多关于Apache Hudi的文章,但很多文章在阐述时仅仅将Hudi当做一种表格式,这引发了社区的思考,思考Hudi的愿景到底是什么,并且在Hudi社区发起了..._hudi 论坛

随便推点

最新飞翔福袋脚本源码分享(Autojs完整源码)_QQ_1707452246的博客-程序员宅基地

function getlucky() { log("开始红包") let gz = boundsFindCon("关注", device.width * 0.35, device.height * 0.35, device.width * 0.65, device.height * 0.65, -1) let jz = boundsFindCon("价值", device.width * 0.35, device.height * 0.5, device.width * 0.65._福袋脚本

3 数据可视化大屏 - 布局: BootStrap 之容器Container_bootstrap container容器_YYDataV数据可视化的博客-程序员宅基地

目录Bootstrap 有三个类型的容器实战.container 演练.container-fluid 演练.container-sm .container-md .container-lg 演练.container-xl.container-xxl 演练Sass 映射 - 修改预定义的容器类可视化大屏设计 - 总目录导读:大屏案例参考YYDatav的数据可视化大屏《精彩案例汇总》(Python&Echarts源码)_YYDataV的博客-程序员宅基地._bootstrap container容器

Powershell管理系列(七)删除Exchange用户邮箱中多余的电子邮件地址_weixin_33863087的博客-程序员宅基地

继续找类似的文章,发现网上还是有在windows server 2012环境下使用admodify的案例,见如下链接,http://www.dotblogs.com.tw/swater111/archive/2013/06/25/106062.aspx,下面就开始着手演示下三种修改邮箱地址方法的操作步骤。第一种、使用Powershell+Exce...