BIO_push()详解

来源:互联网 发布:通用工会财会软件 编辑:程序博客网 时间:2024/06/08 18:15
BIO的callback函数是实现BIO多态性的一个关键因素之一。
BIO提供的callback控制系列函数有五个,都是一些宏定义,下面是它的声明和定义(openssl/bio.h):
     #define BIO_set_callback(b,cb)         ((b)->callback=(cb))
     #define BIO_get_callback(b)            ((b)->callback)
     #define BIO_set_callback_arg(b,arg)    ((b)->cb_arg=(char *)(arg))
     #define BIO_get_callback_arg(b)                ((b)->cb_arg)
其中,callback函数本身的声明如下:
     typedef long callback(BIO *b, int oper, const char *argp,
                            int argi, long argl, long retvalue);
此外,还有一个用于调试目的的函数,其实声明如下:
     long BIO_debug_callback(BIO *bio,int cmd,const char *argp,int argi,long argl,long ret);
如果要看具体的例子,那么在文件crypto/bio/bio_cb.c的函数BIO_debug_callback() 本身就是一个非常好的例子。
下面,我们从callback函数本身开始分别简单介绍这些函数的作用。

【callback】
callback 函数在BIO中非常重要,许多控制功能都是要通过callbank函数协助完成的,比如BIO要执行释放的操作BIO_free,那么其实它是先调用 callback函数设置下面的操作将是释放操作(控制码:BIO_CB_FREE),然后才调用别的相关函数执行真正的操作,在后面我们会列出这些控制功能函数,并简单说明callback函数是怎么在这些功能的实现中使用的。现在,我先简单介绍callback函数的各个参数:
(参数名字参看说明的函数的声明)
参数---b
这是callback函数的输入参数,也就是callback函数对应的BIO
参数---oper
设置BIO将要执行的操作,有些操作,callback函数将被调用两次,一次实在实际操作之前,一次实在实际操作之后,在后面的调用的时候,一般是将 oper和BIO_CB_RETURN相或操作后作为参数的。也就是说,后一次调用的时候oper参数应该使用oper|BIO_CB_RETURN。
参数---argp, argi,argl
这些参数根据oper定义的操作的不同而不一样,是在相应操作中要用到的参数。
参数---retvalue
这是默认的callback函数返回值,也就睡说,如果没有提供BIO没有提供相应的callback函数,那么就会返回这个值。真正的返回值是 callback函数本身提供的。如果在实际的操作之前调用callback函数,并且这时候retvalue参数设置为1,如果callback的函数返回值无效,那么对callback函数的调用就会导致程序立刻返回,BIO的操作就不会执行。
一般情况下,callback函数在执行完后都应该返回retvalue的值,除非该操作有特别的目的要修改这个返回值。
下面简单列出我们比较熟悉的一些跟callback函数相关的BIO函数使用callback函数的情况:
1.BIO_free(b)
在执行该操作之前,调用了callback(b, BIO_CB_FREE, NULL, 0L, 0L, 1L)
2.BIO_read(b,out,outl)
在执行该操作之前,调用了callback(b, BIO_CB_READ, out, outl, 0L, 1L),之后调用了callback(b, BIO_CB_READ|BIO_CB_RETURN, out, outl, 0L,retvalue),大家可以看到,这就是我们上面说明过的情况,即两次调用callback的操作,后面一次oper的参数需要或上 BIO_CB_RETURN。
3.BIO_write(b,in,inl)
在执行该操作之前,调用了callback(b, BIO_CB_WRITE, in, inl, 0L, 1L),之后调用了callback(b, BIO_CB_WRITE|BIO_CB_RETURN, in, inl, 0L, retvalue) 
4.BIO_gets(b,out,outl)
在执行该操作之前,调用了callback(b, BIO_CB_GETS, out, outl, 0L, 1L),之后调用了callback(b, BIO_CB_GETS|BIO_CB_RETURN, out, outl, 0L, retvalue)
5.BIO_puts(b, in)
在执行该操作之前,调用了callback(b, BIO_CB_WRITE, in, 0, 0L, 1L) ,之后调用了callback(b, BIO_CB_WRITE|BIO_CB_RETURN, in, 0, 0L,retvalue) 
6.BIO_ctrl(BIO *b, int cmd, long larg, void *parg)
在执行该操作之前,调用了callback(b,BIO_CB_CTRL,parg,cmd,larg,1L),之后调用了callback(b,BIO_CB_CTRL|BIO_CB_RETURN,parg,cmd, larg,ret)

【BIO_set_callback和BIO_get_callback】
这两个函数用于设置和返回BIO中的callback函数,它们都是宏定义,根据前面的叙述我们已经知道,callback函数在许多高层的操作中都使用了,因为它能用于调试跟踪的目的或更改BIO的操作,具有很大的灵活性,所以这两个函数也就有用武之地了。
【BIO_set_callback_arg和IO_get_callback_arg】
顾名思义,这两个函数用了设置和得到callback函数中的参数。
【BIO_debug_callback】
这是一个标准的调试信息输出函数,它把相关BIO执行的所有操作信息都打印输出到制定的地方。如果callback参数没有指定输出这些信息的BIO口,那么就会默认使用stderr作为信息输出端口。



BIO系列之6---BIO的IO操作函数

(作者:DragonKing Mail:wzhah@263.net 发布于:http://openssl.126.com之openssl专业论坛)

这些函数是BIO的基本读写操作函数,包括四个,他们的定义如下(openssl/bio.h):
     int    BIO_read(BIO *b, void *buf, int len);
     int    BIO_gets(BIO *b,char *buf, int size);
     int    BIO_write(BIO *b, const void *buf, int len);
     int    BIO_puts(BIO *b,const char *buf);

【BIO_read】
从BIO接口中读出指定数量字节len的数据并存储到buf中。成功就返回真正读出的数据的长度,失败返回0或-1,如果该BIO没有实现本函数则返回-2。

【BIO_gets】
该函数从BIO中读取一行长度最大为size的数据。通常情况下,该函数会以最大长度限制读取一行数据,但是也有例外,比如digest型的BIO,该函数会计算并返回整个digest信息。此外,有些BIO可能不支持这个函数。成功就返回真正读出的数据的长度,失败返回0或-1,如果该BIO没有实现本函数则返回-2。需要注意的时,如果相应的BIO不支持这个函数,那么对该函数的调用可能导致BIO链自动增加一个buffer型的BIO。

【BIO_write】
往BIO中写入长度为len的数据。成功就返回真正写入的数据的长度,失败返回0或-1,如果该BIO没有实现本函数则返回-2。

【BIO_puts】
往BIO中写入一个以NULL为结束符的字符串,成功就返回真正写入的数据的长度,失败返回0或-1,如果该BIO没有实现本函数则返回-2。

需要注意的是,返回指为0或-1的时候并不一定就是发生了错误。在非阻塞型的source/sink型或其它一些特定类型的BIO中,这仅仅代表目前没有数据可以读取,需要稍后再进行该操作。
有时候,你可能会使用了阻塞类型的sokect使用的一些系统调用技术(如select,poll,equivalent)来决定BIO中是否有有效的数据被read函数读取,但建议不要在阻塞型的接口中使用这些技术,因为这样的情况下如果调用BIO_read就会导致在底层的IO中多次调用read函数,从而导致端口阻塞。建议select(或equivalent)应该和非阻塞型的IO一起使用,可以在失败之后能够重新读取该IO,而不是阻塞住了。
关于BIO的IO操作为什么会失败以及怎么处理这些情况请参加BIO_should_retry()函数的说明文档。



BIO系列之7---BIO链的操作

(作者:DragonKing Mail:wzhah@263.net 发布于:http://openssl.126.com之openssl专业论坛)

我在介绍BIO结构的时候说过,BIO结构其实是一个链式结构,单个BIO是只有一个环节的BIO链的特例,那么我们怎么构造或在一个BIO链中增加一个BIO,怎么从一个BIO链中删除一个BIO呢,那么本节就是专门讲述这个问题的。
在openssl中,针对BIO链的操作包括两个函数(openssl/bio.h):
     BIO *  BIO_push(BIO *b,BIO *append);
     BIO *  BIO_pop(BIO *b);

【BIO_push】
该函数把参数中名为append的BIO附加到名为b的BIO上,并返回b。其实,openssl作者本身也认识到,BIO_push的函数名字可能会导致误会,因为BIO_push函数其实只是将两个BIO连接起来,而不是Push的功能,应该是join才对。
我们举几个简单的例子说明BIO_push的作用,假设md1、md2是digest类型的BIO,b64是Base64类型的BIO,而f是file类型的BIO,那么如果执行操作
 BIO_push(b64, f);
那么就会形成一个b64-f的链。然后再执行下面的操作:
      BIO_push(md2, b64);
      BIO_push(md1, md2);
那么就会形成md1-md2-b64-f的BIO链,大家可以看到,在构造完一个BIO后,头一个BIO就代表了整个BIO链,这根链表的概念几乎是一样的。
这时候,任何写往md1的数据都会经过md1,md2的摘要(或说hush运算),然后经过base64编码,最后写入文件f。可以看到,构造一条好的BIO链后,操作是非常方便的,你不用再关心具体的事情了,整个BIO链会自动将数据进行指定操作的系列处理。
需要注意的是,如果是读操作,那么数据会从相反的方向传递和处理,对于上面的BIO链,数据会从f文件读出,然后经过base64解码,然后经过md1,md2编码,最后读出。

【BIO_pop】
该函数把名为b的BIO从一个BIO链中移除并返回下一个BIO,如果没有下一个BIO,那么就返回NULL。被移除的BIO就成为一个单个的BIO,跟原来的BIO链就没有关系了,这样你可以把它释放或连接到另一个BIO上去。可以看到,如果是单个BIO的时候,该操作是没有任何意义的。
如果你执行操作:
      BIO_pop(md2);
那么返回值将为b64,而md2从上述的链中移除,形成一个新的md1-b64-f的BIO链,对于数据操作来说,还是往md1读写,没有什么变化,但是底层处理过程已经发生变化了,这就是封装与透明的概念。可以看到,虽然BIO_pop参数只是一个BIO,但该操作直接的后果会对该BIO所在的链产生影响,所以,当BIO所在的链不一样的时候,其结果是不一样的。

此外:BIO_push和BIO_pop操作还可能导致其它一些附加的结果,一些相关的BIO可能会调用一些控制操作,这些具体的细节因为各个类型的BIO不一样,在他们各自的说明中会有说明。
0 0
原创粉丝点击