iOS linphone 405Method not allow

2019-06-19  本文已影响0人  蓝蓝蓝蓝蓝v

iOS linphone进行语音通话时出现以下错误:
1.拨打方 拨出电话,收到405 Method not allowed 导致无法拨出
2.通话成功后,挂断 收到501 Not implement 导致另一方无法挂断

一开始以为都是服务器发出的信令,后来发现其实是linphone自己发出的,服务器只是做了转发。

Linphone 被叫方如何解析来电SIP消息中的自定义头消息

参考以上连接可以了解一下sip消息的解析流程。
下面是问题调试流程
1.首先linphone 源码要是debug编译才能跳转到源码内容。(编译命令 ./prepare.py -c && ./prepare.py --debug && make)

2.在LinphoneManager中断点在 -(void)iterate 这个定时方法中,在控制台输入

b channel.c:592

断点在channel.c 方法中的void belle_sip_channel_parse_stream 方法中
点击传入的参数obj,可以看到obj->input_stream下buff属性为sip信令的文本展示


image.png

可以看到这个sip信令中开头出现了很多hello。所以导致linphone解析信令的时候出错了。有时信令正常,没有多余的hello,就可以正常拨出,一旦多了hello就会出现405 Method not allowed。

对于405 的信令发出,在sal.cpp line 94 ,这里对method进行了匹配处理,当method 为hellohelloINVITE的时候就会匹配不到返回405 信令。


image.png

其实对sip信令 开头有多余的字符,linphone有处理的地方,在channel.c line555通过get_message_start_pos方法计算得到偏移offset。
问题就是出现在了这个get_message_start_pos方法中。方法实现在channel.c line232

static int get_message_start_pos(char *buff, size_t bufflen) {
    /*FIXME still to optimize and better test, specially REQUEST PATH and error path*/
    int i;
    int res=0;
    int status_code;
    char method[17]={0};
    char saved_char1;
    char sip_version[10]={0};
    size_t saved_char1_index;

    for(i=0; i<(int)bufflen-12;i++) { /*9=strlen( SIP/2.0\r\n)*/
        switch (buff[i]) { /*to avoid this character to be ignored by scanf*/
            case '\r':
            case '\n':
            case ' ' :
            case '\t':
                continue;
            default:
                break;
        }
        saved_char1_index=bufflen-1;
        saved_char1=buff[saved_char1_index]; /*make sure buff is null terminated*/
        buff[saved_char1_index]='\0';
        res=sscanf(buff+i,"SIP/2.0 %d ",&status_code);
        if (res!=1) res=sscanf(buff+i,"HTTP/1.%*i %d ",&status_code); /*might be HTTP ?*/
        if (res!=1) {
            res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2
                    && is_token(method,sizeof(method))
                    && (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0);
        }
        buff[saved_char1_index]=saved_char1;
        if (res==1) return i;
    }
    return -1;
}

在这个方法中数组method被申明了17的长度,所以当sip信令前面有很多hello的时候,method会被解析成 hellohelloINVITE。method的解析是通过c函数sscanf

res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2
                    && is_token(method,sizeof(method))
                    && (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0);

最后我的解决办法是 去除掉多余的hello。代码如下

//判断是否包含某个字符串
_Bool isCoincide(char *a, char *p)
{
    char *ptemp = p;
    while (*a != '\0')
    {
        if (*a == *p)
        {
            a++;
            p++;
        }
        else
        {
            a++;
            p = ptemp;
        }
        if (*p == '\0')
        {
            return 1;
        }
    }
    return 0;
}

static int get_message_start_pos(char *buff, size_t bufflen) {
    /*FIXME still to optimize and better test, specially REQUEST PATH and error path*/
    int i;
    int res=0;
    int status_code;
    char method[17]={0};
    char saved_char1;
    char sip_version[10]={0};
    size_t saved_char1_index;

    for(i=0; i<(int)bufflen-12;i++) { /*9=strlen( SIP/2.0\r\n)*/
        switch (buff[i]) { /*to avoid this character to be ignored by scanf*/
            case '\r':
            case '\n':
            case ' ' :
            case '\t':
                continue;
            default:
                break;
        }
        saved_char1_index=bufflen-1;
        saved_char1=buff[saved_char1_index]; /*make sure buff is null terminated*/
        buff[saved_char1_index]='\0';
        res=sscanf(buff+i,"SIP/2.0 %d ",&status_code);
        if (res!=1) res=sscanf(buff+i,"HTTP/1.%*i %d ",&status_code); /*might be HTTP ?*/
        if (res!=1) {
//            res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2
//                    && is_token(method,sizeof(method))
//                    && (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0);
            
            res = (sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2);
            if (res==1) {
                //去除hello
                _Bool j;
                j = isCoincide(method, "hello");
                if(j==1){
                    //包含hello
                    belle_sip_message("sip信令包含hello 执行去除");
                    do{
                        //执行去除
                        sscanf(buff+i+5,"%16s %*s %9s\r\n",method,sip_version);
                        j=isCoincide(method, "hello");
                        i+=5;
                    }while(j>0);
                }
                
                res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2
                        && is_token(method,sizeof(method))
                        && (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0);
            }
            
        }
        buff[saved_char1_index]=saved_char1;
        if (res==1) return i;
    }
    return -1;
}

新增了一个isCoincide方法判断字符串是否包含hello
然后通过sscanf多次匹配 去除hello
最后返回正确的i 即为正确的偏移量offset
重新编译后替换工程中的sdk

通过这样,解决了405 和501 的问题,后来发现除了invite、bye会出现多余的hello,有的时候ack也有多余的hello,不过通过这样都解决了。

上一篇 下一篇

猜你喜欢

热点阅读