您的位置:首页 > 其它

sip phone 日志7

2004-11-29 17:51 399 查看
SIP消息解析设计<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

    因sip的协议复杂,需逐个击破,这次我们开始对其协议进行语法分析

1)    sip包括两种消息:产生和处理

a)      产生请求包括 请求行 消息头 空行 消息体

b)      处理请求包括 状态行 消息头 空行 消息体

2)    请求行包括 Method  Request-URI  SIP-VERSION

a)      Method包括:

l        INVITE

l        ACK   

l        CANCEL

l        REGISTE

l        BYE   

l        OPTIONS

3)    状态行包括 SIP-VERSION  STATUS-CODE  Reason-Phrase

4)    消息头包括多个消息行

5)    消息头类型

a)      vias

b)      froms

c)      tos

d)      call_ids

e)      cseqs

f)      max_forwards

g)      contacts

h)      content_types

i)      content_lengths

j)      supported

k)      require

我们刚开始以简单的消息来分析:
INVATE sip:Bob.Johnson@company.com SIP/2.0
Via: SIP/2.0/UDP workstation1000.university.com:5060
From: Laura Brown <sip:Laura.Brown@university.com>
To: Bob Johnson <sip:Bob.Johnson@company.com>
Call-ID: 12345678@workstation1000.university.com
CSeq: 1 INVATE
Contact: Laura Brown <sip:Laura@workstation1000.university.com>
Content-Type:  application/sdp
Content-Length:154

以上SIP消息中可能出现的数据类型:
整形,浮数型,字符串,Email,域名,随机串,空行,空格等…

下面我们先把相应正则表达式写好:

1.      ws         [ /t]+            //空格

2.      nl            /n            

3.      whitespace    ^[ /t]*/n          //空行

4.      url       ([^ /t/</>]*)@([^ /t/</>]*)  

5.      qstring   /"[^/"/n]*[/"/n]  //匹配”dfdfd”

6.      commpro   UDP|TCP|TLS|SCTP  //通讯协议

7.      float     [0-9]*.[0-9]*

8.      integer   [0-9]+

9.      threenum  [0-9]{3}  //三位整数

10.  string        [a-zA-Z][a-zA-Z0-9]+

11.  field     [a-zA-Z0-9.]+

12.  name      [a-zA-Z]+[ ]*[a-zA-Z]*

13.  localid   [a-zA-Z0-9-]+@[a-zA-Z0-9.]+
这几个表达式都比较简单,读者可参考正则表达式对照。

这里先介始一下lex和yacc,Window下开发的朋友可能有点陌生,但在Xnix下很通行.lex是词法识别工具,yacc是语法识别工具.相结起来就可完成一种计算机语言的编译程序.具体这就不展开介绍,可参考:

l        www-900.ibm.com/developerWorks/ cn/linux/sdk/lex/index.shtml

l        http://dinosaur.compilertools.net/
l        《lex与yacc(第二版)》

通过使用这两个工具可以快速解析SIP消息.并且因lex和yacc可生成C代码,我们可以很快移植到我们程序代码中。

首先我们来写lex:
**********************************************************
%{
#include <string.h>

extern int lineno;

%}
ws   [ /t]+

nl         /n

whitespace  ^[ /t]*/n

url   ([^ /t/</>]*)@([^ /t/</>]*)

qstring    /"[^/"/n]*[/"/n]

commpro  UDP|TCP|TLS|SCTP

float   [0-9]*.[0-9]*

integer  [0-9]+

threenum [0-9]{3}

str     [a-zA-Z][a-zA-Z0-9]+

field   [a-zA-Z0-9.]+

name   [a-zA-Z]+[ ]*[a-zA-Z]*

localid  [a-zA-Z0-9-]+@[a-zA-Z0-9.]+

 

 

%%
{ws} ;

{whitespace}    { return WHITESPACE; }

{url}   { yylval.string = strdup(yytext); return URL; }

{qstring}  { yylval.string = strdup(yytext+1); /* skip open quote */
             if(yylval.string[yyleng-2] != '"')
          warning("Unterminated character string",(char *)0);
     else
          yylval.string[yyleng-2] = '/0'; /* remove close quote */
             return QSTRING;
           }

{commpro}   { yylval.string = strdup(yytext); return COMMPRO; }

{float}   { yylval.floatval = atof(yytext); return FLOAT; }

{integer}  { yylval.intval = atoi(yytext); return INTEGER; }

{threenum} { yylval.intval = atoi(yytext); return THREENUM; }

{str}   { yylval.string = strdup(yytext); return STR; }

{field}  { yylval.string = strdup(yytext); return FIELD; }

{name}   { yylval.string = strdup(yytext); return NAME; }

{localid}  { yylval.string = strdup(yytext); return LOCALID; }
         
         
/*command*/         
INVITE     { return INVITE; }
ACK        { return ACK; }
CANCEL     { return CANCEL; }
REGISTER   { return REGISTER; }
BYE        { return BYE; }
OPTIONS    { return OPTIONS; }

/*msg head*/
Via:        { return VIA; }
From:       { return FROM; }
To:         { return TO; }
Call-ID:    { return CALLID; }
CSeq:       { return CSEQ; }
Max-Forwards:  { return MAXFORWARDS; }
Contact:    { return CONTACT; }
Content_type: { return CONTACTTYPE; }
Content_length: { return CONTACTLENGTH; }
Supported: { return SUPPORTED; }
Require: { return REQUIRE; }

 

{nl}       { lineno++; }
.          { return yytext[0]; }
%%

***********************************************************************

写好lex后,我们就来把前面的语法分析变成yacc代码:
*************************************************************
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
%}

%union {
   
    char    *string;     /* string buffer */
    int     intval;
    double  floatval;
    int     cmd;          /* command value */
    int     headfield;
}

 

%token <string>  QSTRING LOCALID COMMPRO URL FIELD NAME STR
%token <cmd>   INVITE ACK CANCEL REGISTER BYE OPTIONS
%token <headfield>  VIA FROM TO CALLID CSEQ MAXFORWARD CONTACT CONTACTTYP CONTACTLEN SUPPORTED REQUIRE
%token <floatval> FLOAT
%token <intval>  INTEGER THREENUM

%type <intval>  lineno
%%

 

start: sip_msg
 ;
 
/*sip包括两种消息:产生和处理*/
sip_msg: generating_request
 | sending_request
 ;
 
/***************************************************************/
/*请求包括 请求行 消息头 空行 消息体 */
generating_Request: req_line msg_head SPACELINE msg_body
 | start_line msg_heads
 ;

/*处理包括 状态行 消息头 空行 消息体 */
sending_request: status_line msg_head SPACELINE msg_body
/***************************************************************/

/***************************************************************/
/*请求行包括 Method  Request-URI  SIP-VERSION  */
req_line: command require_url sip_ver
 ;

/*状态行包括 SIP-VERSION  STATUS-CODE  Reason-Phrase */
status_line: sip_ver status_code reason_phrase
 ;
/***************************************************************/

/***************************************************************/
/*消息体包括多个消息行*/
msg_heads: msg_head
 |msg_heads msg_head
 ; /*可多个 如:msg_heads: via from to 或msg_head: via from to cseq call_id*/

//msg_head: vias froms tos call_ids cseqs max_forwards contacts content_types content_lengths
 ;

/*头域类型*/
msg_head: vias /*任何一个*/
 | froms
 | tos
 | call_ids
 | cseqs
 | max_forwards
 | contacts
 | content_types
 | content_lengths
 | supported
 | require
 ;
/***************************************************************/

/********************************************************************/

vias:via /*一个或多个*/
 | vias via
 ;
via: VIA sip_ver_pro field
 ;
 
froms: from /*一个*/
 ;
from: FROM NAME require_url
 ;
 
tos: to /*一个*/
 ;
to: TO NAME require_url
 ;
 
call_ids: call_id /*一个*/
 ;
call_id: CALL_ID URL
 ;
 
cseqs:    cseq /*一个*/
 ;
cseq: CSEQ INTEGER command
 ;
 
max_forwards: max_forward /*一个*/
 ;
max_forward: MAXFORWARDS INTEGER
 ;

 

contacts:  /*空或一个*/
 | contact
 ;
contact:  CONTACT NAME require_url
 ;
 
content_types: /*空或一个*/
 | content_type
 ;
content_type:
 | media_type
 ;

content_lengths: /*空或一个*/
 | content_length
content_length: CONTACTLENGTH INTEGER
 ;
 
/********************************************************************/

 

 

/********************************************************************/
command:  INVITE
 | ACK
 | CANCEL
 | REGISTER
 | BYE
 | OPTIONS
 ;

status_code: THREENUM
 ;

reason_phrase: STR
 ;

require_url: sip ":" URL { printf(" %s ",$2); }
 | "<" sip URL ">"
 ;
sip_ver: "SIP/" INTEGER { printf(" %d ",$2); }
 ;

sip: sip
 |sips
 ;

sip_ver_pro: sip_ver "/" commpro
 ;
 
 
field:  FIELD ":" INTEGER
 ;
 
media-type: STR "/" STR
 ;
/********************************************************************/

 

makefile:
*******************************
CC = gcc
LIBS = -ly -ll -lm
LEX = flex
YACC = yacc
CFLAGS = -DYYDEBUG=1

SipAnalyse:y.tab.o lex.yy.o
 $(CC) $(CFLAGS) -o SipAnalyse y.tab.o lex.yy.o $(LIBS)
 
lex.yy.o: lex.yy.c y.tab.h

y.tab.c y.tab.h: sip.y
 $(YACC) -d sip.y
 
lex.yy.c: sip.l
 $(LEX) sip.l
****************************************
以上代码还没有检查,如有问题请回复,有兴趣者在此基础上进行修改。
                                                                                                   大大狗
                                                                                                yirui1@21cn.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息