SIP消息解析设计
因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