BIO控制函数介绍
---根据openssl doc/crypto/bio/bio_ctrl.pod翻译和自己的理解写成
(作者:DragonKing Mail:wzhah@263.net 发布于:http://gdwzh.126.com openssl专业论坛)
BIO控制函数有许多,并且不同的BIO类型还有不同的控制函数,这里只简单介绍一些通用的BIO控制函数,至于某种类型BIO的特定控制函数,则参考后续的文件。
BIO的通用控制函数有以下几种,其声明如下(openssl/bio.h):
long BIO_ctrl(BIO *bp,int cmd,long larg,void *parg);
long BIO_callback_ctrl(BIO *b, int cmd, void (*fp)(struct bio_st *, int, const char *, int, long, long));
char * BIO_ptr_ctrl(BIO *bp,int cmd,long larg);
long BIO_int_ctrl(BIO *bp,int cmd,long larg,int iarg);
int BIO_reset(BIO *b);
int BIO_seek(BIO *b, int ofs);
int BIO_tell(BIO *b);
int BIO_flush(BIO *b);
int BIO_eof(BIO *b);
int BIO_set_close(BIO *b,long flag);
int BIO_get_close(BIO *b);
int BIO_pending(BIO *b);
int BIO_wpending(BIO *b);
size_t BIO_ctrl_pending(BIO *b);
size_t BIO_ctrl_wpending(BIO *b);
其实,在这些函数中,除了BIO_ctrl,BIO_callback_ctrl,BIO_ptr_ctrl,BIO_int_ctrl,BIO_ctrl_pending,BIO_ctrl_wpending是真正的函数外,其它都是宏定义,而且,在这些函数中,除了BIO_ctrl,BIO_callback_ctrl,其它基本上都是简单的BIO_ctrl输入不同的参数的调用。下面就一个一个介绍这些函数。
【BIO_ctrl】
从上面的叙述可以知道,BIO_ctrl是整个控制函数中最基本的函数,它支持不同的命令输入,从而产生不同的功能,由此,它也就衍生了许多其它函数,作为一个比较地层的控制函数,一般来说用户并不需要直接调用它,因为在它之上已经使用宏定义和函数调用的形式建造了许多直接面向用户的函数。
filter型的BIO没有定义BIO_ctrl功能,如果对他们调用这个函数,他们就简单的把命令传到BIO链中的下一个BIO。也就是说,通常可以不用直接调用一个BIO的BIO_ctrl函数,只需要在它所在的BIO链上调用该函数,那么BIO链就会自动将该调用函数传到相应的BIO上去。这样可能就会导致一些意想不到的结果,比如,在目前的filter型BIO中没有实现BIO_seek()函数(大家待会就会明白BIO_seek就是BIO_ctrl的简单宏定义),但如果在这个BIO链上的末尾是一个文件或文件描述符型BIO,那么这个调用也会返回成功的结果。
对于source/sink型BIO来说,如果他们不认得BIO_ctrl所定义的操作,那么就返回0。
【BIO_callback_ctrl】
这个函数是这组控制函数中唯一一个不是通过调用BIO_ctrl建立起来的,它有自己的实现函数,而且跟BIO_ctrl毫不相干。跟BIO_ctrl一样,它也是比较底层的控制函数,在它上面也定义了一些直接面向用户的控制函数,一般来说,用户不需要直接调用该函数。
需要说明的是,该函数和BIO_ctrl函数为了实现不同类型BIO具有不同的BIO_ctrl控制功能,他们的操作基本上都是由各个BIO的callback函数来定义的。这是不同的BIO能灵活实现不同功能的根本所在。
【BIO_ptr_ctrl和BIO_int_ctrl】
这两个函数都是简单的调用了BIO_ctrl函数,不同的是,后者是输入了四个参数并传入到BIO_ctrl函数中,简单返回了调用BIO_ctrl返回的返回值;而前者只输入了三个参数,最后一个BIO_ctrl参数是作为输出参数并作为返回值的。
【BIO_reset】
该函数是BIO_ctrl的宏定义函数,为了大家对BIO_ctrl的宏定义函数有一个感性的认识,我把这个宏定义写出来,如下:
#define BIO_reset(b) (int)BIO_ctrl(b,BIO_CTRL_RESET,0,NULL)
这就是BIO_ctrl的典型宏定义方式,它通过这种方式产生了大量的控制函数。顾名思义,BIO_reset函数只是简单的将BIO的状态设回到初始化的时候的状态,比如文件BIO,调用该函数就是将文件指针指向文件开始位置。
一般来说,调用成功的时候该函数返回1,失败的时候返回0或-1;但是文件BIO是一个例外,成功调用的时候返回0,失败的时候返回-1。
【BIO_seek】
该函数也是BIO_ctrl的宏定义函数,其定义如下:
#define BIO_seek(b,ofs) (int)BIO_ctrl(b,BIO_C_FILE_SEEK,ofs,NULL)
该函数将文件相关的BIO(文件和文件描述符类型)的文件指针知道距离开始位置ofs(输入参数)字节的位置上。调用成功的时候,返回文件的位置指针,否则返回-1;但是文件BIO例外,成功的时候返回0,失败的时候返回-1。
【BIO_tell】
该函数也是BIO_ctrl的宏定义函数,其定义如下:
#define BIO_tell(b) (int)BIO_ctrl(b,BIO_C_FILE_TELL,0,NULL)
该函数返回了文件相关BIO的当前文件指针位置。跟BIO_seek一样,调用成功的时候,返回文件的位置指针,否则返回-1;但是文件BIO例外,成功的时候返回0,失败的时候返回-1。
【BIO_flush】
该函数也是BIO_ctrl的宏定义函数,其定义如下:
#define BIO_flush(b) (int)BIO_ctrl(b,BIO_CTRL_FLUSH,0,NULL)
该函数用来将BIO内部缓冲区的数据都写出去,有些时候,也用于为了根据EOF查看是否还有数据可以写。调用成功的时候该函数返回1,失败的时候返回0或-1。之所以失败的时候返回0或者-1,是为了标志该操作是否需要稍后以跟BIO_write()相同的方式重试。这时候,应该调用BIO_should_retry()函数,当然,正常的情况下该函数的调用应该是失败的。
【BIO_eof】
该函数也是BIO_ctrl的宏定义函数,其定义如下
#define BIO_eof(b) (int)BIO_ctrl(b,BIO_CTRL_EOF,0,NULL)
如果BIO读到EOF,该函数返回1,至于EOF的具体定义,根据BIO的类型各不相同。如果没有读到EOF,该函数返回0.
【BIO_set_close】
该函数也是BIO_ctrl的宏定义函数,其定义如下:
#define BIO_set_close(b,c) (int)BIO_ctrl(b,BIO_CTRL_SET_CLOSE,(c),NULL)
该函数设置BIO的关闭标志,该标志可以为BIO_CLOSE或BIO_NOCLOSE。一般来说,该标志是为了指示在source/sink型BIO释放该BIO的时候是否关闭其下层的I/O流。该函数总是返回1。
【BIO_get_close】
该函数也是BIO_ctrl的宏定义函数,其定义如下:
#define BIO_get_close(b) (int)BIO_ctrl(b,BIO_CTRL_GET_CLOSE,0,NULL)
该函数读取BIO的关闭标志,返回BIO_CLOSE或BIO_NOCLOSE。
【BIO_pending、BIO_wpending、BIO_ctrl_pending和BIO_ctrl_wpending】
这些函数都是用来得到BIO中读缓存或写缓存中字符的数目的,返回相应缓存中字符的数目。
前面两个函数也是BIO_ctrl的宏定义函数,其定义如下:
#define BIO_pending(b) (int)BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
#define BIO_wpending(b) (int)BIO_ctrl(b,BIO_CTRL_WPENDING,0,NULL)
后两个函数功能跟他们是一样的,只不过他们是通过调用BIO_ctrl函数实现的,而不是宏定义。此外,前面两个函数返回的是int型,而后面两个函数返回的是size_t型。
需要注意的是,BIO_pending和 BIO_wpending并不是在所有情况下都能很可靠地得到缓存数据的数量,比如在文件BIO中,有些数据可能在文件内部结构的缓存中是有效的,但是不可能简单的在BIO中得到这些数据的数量。而在有些类型BIO中,这两个函数可能还不支持。基于此,openssl作者本身也建议一般不要使用这两个函数,而是使用后面两个,除非你对你所做的操作非常清楚和了解。