基于bfin-uclinux的asterik siptls

来源:互联网 发布:卖家开通淘宝直播条件 编辑:程序博客网 时间:2024/06/07 09:35

    博客N久没上了,平时太懒了。明天就放假了,事都忙完了,时间不能浪费,博客空空如也,现在有空就写写之前asterisk的siptls问题,说不定对看到这篇博文的你有用,对于这方面的资料实现太少,百度没,google没,当然碰到这些问题那个郁闷啊,不过还好,问题都解决了。下面说说这一路的过程
    刚开始接到上头说让我看看配置asterisk的siptls,稍微了解了下TLS后上voip-info.org上查看如何配置,按上面步骤一步一步下来,没成功,证书生成问题,在网上搜索了下配置asterisk的TLS证书,重新生成证书,在PC上可以用。SIP话机TLS连接上,通话正常,trunk用TLS也正常。以为就这样简简单单结束了,谁知放在板卡上,TLS无法用了。板卡系统是uclinux,交叉编译工具用的是bfin-linux-uclibc。想想没问题啊。PC上都好好的。CLI上输出警告FILE * open failed!,有警告说明有问题,这就好办啦,看了下源码,handle_tcptls_connection函数中tcptls_session->f创建失败为空。什么原因导致?加几个打印信息,条件编译DO_SSL中的都没走,宏DO_SSL没定义,有个头文件有定义DO_SSL,但有条件,当时根本不懂这那些条件有什么用,自己直接宏定义,仍提示警告。仔细看源码发现是创建tcptls_session->f有两种方法,funopen和fopencookie,分别是BSD接口和linux的接口,豪无疑问是用linux接口。fopencookie在宏定义HAVE_FOPENCOOKIE下,看相关头文件中定义的#undef HAVE_FOPENCOOKIE,先不管3721先定义宏HAVE_FOPENCOOKIE,编译问题出来了,未定义fopencookie函数。libc.a中根本没有fopencookie函数。google后才晓得fopencookie是glibc中的函数。blackfin的工具链中的uclibc库默认是不支持fopencookie函数的(之前的DO_SSL没定义就是因为#undef HAVE_FOPENCOOKIE造成),因此得自己编译uclibc替换工具链中的相应库了。编译uclibc库中间遇到了一些波折不过最终也编译过了。函数支持了,以为这下总可以用了吧,不过还是不行,TLS分机能注册但通话有问题,中间又捣鼓了好久,确认不是新的uclibc问题,openssl和证书问题,最后就从代码着手,每次在建立通话时,ssl_read打印的信息显示ssl read size 1 returns 0 <>,建立的线程就被cleanup了。拿实际数据来说。
chan_sip.c
static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session)
{
//...以上省略
if (sscanf(get_header(&reqcpy, "Content-Length"), "%30d", &cl)) {
                                while (cl > 0) {
                                        size_t bytes_read;
                                        ast_mutex_lock(&tcptls_session->lock);
                                        if (!(bytes_read = fread(buf, 1, MIN(sizeof(buf) - 1, cl), tcptls_session->f))) {
                                                ast_mutex_unlock(&tcptls_session->lock);
                                                goto cleanup;
                                        }
                                        buf[bytes_read] = '/0';
                                        ast_mutex_unlock(&tcptls_session->lock);
                                        if (me->stop)
                                                goto cleanup;
                                        cl -= strlen(buf);
                                        ast_str_append(&req.data, 0, "%s", buf);
                                        req.len = req.data->used;
                                }
                        }
//...以下省略
}
tcptls.c
static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
{
        int i = SSL_read(cookie, buf, len-1);
#if 0
        if (i >= 0)
                buf[i] = '/0';
        ast_verb(0, "ssl read size %d returns %d <%s>/n", (int)len, i, buf);
#endif
        return i;
}
假如content-length为395,循环每一次fread(buf, 1, 395, tcptls_session->f),由于ssl_read中只读取len-1的长度,因此只返回394的数据,因此进入循环第二次
fread(buf, 1, 395-394, tcptls_session->f),ssl_read,由于len-1,问题就出现了,我们想要读取这最后的1字节,但并未读取。
因此需要修改ssl_read,将其改为i = SSL_read(cookie, buf, len<2?len,len-1),避免最后一字节未得到。改完后,TLS功能完全可用了。下面总结下blackfin平台下asterisk实现TLS功能的方法。
一、编译uclibc
我的交叉编译工具用的是bfin-linux-uclibc。
将uClibc包中extra/Configs/Config.defaultfdpic.default移到uClibc目录下的.config
make menuconfig
String and Stdio Support  --->
[*] Support fmemopen(), open_memstream(), and fopencookie() (glibc-compat)

编译完后,make install到bfin-linux-uclibc中的相应目录下。

二、修改asterisk的tcptls.c
static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
{
        int i = SSL_read(cookie, buf, len<2?len:len-1);
#if 0
        if (i >= 0)
                buf[i] = '/0';
        ast_verb(0, "ssl read size %d returns %d <%s>/n", (int)len, i, buf);
#endif
        return i;
}


三、重编整个工程,包括uclinux,asterisk。

四、用openssl生成证书,具体方法voip-info.org上有。

五、分机配置asterisk
tlsenable=yes
tlsbindaddr=0.0.0.0:5061
tlscertfile=/etc/asterisk/certs/asterisk.pem
tlsdontverifyserver=yes
tlscipher=DES-CBC3-SHA

[100]
transport=tls
注册台分机试了下可用,而tls trunk配置后也可呼入正常,一切OK。

注册TLS trunk
register = tls://user:pass@domain

PS:其实ssl_read函数根本没必要采取len-1的方式,因为读取大小完全可以在外部调用时控制。ssl_read的问题在电脑上没有影响,电脑系统是ubtun10.04。我猜测可能是uclibc与glibc在对fread处理有些不同,后面好好看下uclibc和glibc源码中fread的实现方式。
以上几个步骤看上去简简单单,但其中的曲折只有自己能体会,从中也学到了许多东西,年关将至,今年的问题没遗留到明年,心里的疙瘩也没了,准备好好回家过年喽。