美文网首页
iOS linphone 405Method not allow

iOS linphone 405Method not allow

作者: 蓝蓝蓝蓝蓝v | 来源:发表于2019-06-19 15:16 被阅读0次

    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,不过通过这样都解决了。

    相关文章

      网友评论

          本文标题:iOS linphone 405Method not allow

          本文链接:https://www.haomeiwen.com/subject/pickqctx.html