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:[email protected] SIP/2.0
Via: SIP/2.0/UDP workstation1000.university.com:5060
From: Laura Brown <sip:[email protected]>
To: Bob Johnson <sip:[email protected]>
Call-ID: [email protected]
CSeq: 1 INVATE
Contact: Laura Brown <sip:[email protected]>
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
****************************************
以上代碼還沒有檢查,如有問題請回復,有興趣者在此基礎上進行修改。
大大狗
[email protected]