freeswitch系列31註冊流程

  1. freeswitch中的sip架構

 

freeswitch的結構圖如上,藍色箭頭是調用,綠色箭頭是回調。最底層的是第三方庫,一個sip協議棧,實現sip信令協議,構造sip當中的各個字段。核心層主要維護跟會話,通路相關的信息,它不依賴於sip,但是會被邏輯層用到。mod_sofia是一個端點模塊,它可以理解爲邏輯層,實現註冊、呼叫等業務,它一方面跟sip協議棧交互,調用sip協議棧去發送信令,提供回調,用於接收信令、狀態變化。另一方面,它也通過核心層,構建session和channel,並且還是通過設置回調,監控通道的狀態改變。

 

  1. 註冊信令流程

 

 

註冊的信令交互比較簡單,就四步:

  1. sip客戶端代理UA先發送register註冊請求;
  2. freeswitch檢查還未認證,回覆401表示未認證,同時生成一個認證摘要(WWW-Authenticate),一起發送給UA。認證機制原理,暫不研究。
  3. UA收到401後,根據自己的用戶名和密碼,以及認證摘要,生成認證信息,然後再次發起註冊請求;
  4. freeswitch檢查通過後,回覆200OK,註冊成功。
  1. register請求
    1. register信令

 

register表示這是一條註冊消息;

via表示sip消息的路由,如果經過多個代理服務器轉發,這裏會有多條記錄

from表示請求端地址

to表示目的端地址

call-id表示本次會話的標識

contact表示聯繫人信息

cseq表示消息序號

allow表示所支持的功能

max-forwards表示可以轉發多少次,防止死循環,類似TTL

user-agent表示UA的型號

expires表示本次註冊的有效期,單位爲秒

content-length表示消息體長度,這裏0表示沒有後續消息體

    1. sofia協議棧

    1. 應用層mod_sofia

之前說過,sip協議棧通知應用是通過回調sofia_event_callback

  1. void sofia_event_callback(nua_event_t event,  
  2.                           int status,  
  3.                           char const *phrase,  
  4.                           nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,  
  5.                           tagi_t tags[])  
  6. {  
  7.     sofia_dispatch_event_t *de;  
  8.   
  9.     //sip消息是一系列nua_開頭的,nua_i表示Indications消息,還有一類nua_r表示Responses消息  
  10.     switch(event) {  
  11.     case nua_i_terminated:  
  12.     case nua_i_invite:  
  13.     case nua_i_register:  
  14.     case nua_i_options:  
  15.     case nua_i_notify:  
  16.     case nua_i_info:  
  17.         ...  
  18.     }  
  19.   
  20.     //invite消息,則要創建session,同時也是創建channel  
  21.     if (event == nua_i_invite && !sofia_private) {  
  22.         session = switch_core_session_request_uuid(sofia_endpoint_interface,...);  
  23.         switch_core_session_thread_launch(session)  
  24.     }  
  25.       
  26.     //最後把sip消息投入到消息隊列mod_sofia_globals.msg_queue  
  27.     sofia_queue_message(de);  

 

這裏雖然對消息有一些判斷,但是沒有具體去處理,只有invite這個消息,會去創建session,最後是投遞到消息隊列mod_sofia_globals.msg_queue 。

 

以前講過,這個隊列主要在消息線程sofia_msg_thread_run處理,不斷地從隊列取出消息,然後調用sofia_process_dispatch_event去處理,這個函數裏面又調用our_sofia_event_callback。

 

  1. our_sofia_event_callback()  
  2. {  
  3.     switch (event) {  
  4.     case nua_i_register:  
  5.         //nua_respond(nh, SIP_200_OK, SIPTAG_CONTACT(sip->sip_contact), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());  
  6.         //nua_handle_destroy(nh);  
  7.         sofia_reg_handle_sip_i_register(nua, profile, nh, &sofia_private, sip, de, tags);  
  8.         break;  
  9.         ...  
  10.     }  
  11. }  

 

our_sofia_event_callback只是個事件分發器,每個消息對應一個處理函數,這裏我們只關心register相關的,調用sofia_reg_handle_sip_i_register處理註冊消息。這個函數在sofia_reg.c,該文件專門處理註冊相關。

 

  1. void sofia_reg_handle_sip_i_register()  
  2. {  
  3.     //處理一些特殊的情況,比如不允許註冊,則回覆403,調用nua_respond可以給客戶端UA發送反饋。  
  4.     if (!(profile->mflags & MFLAG_REGISTER)) {  
  5.         nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());  
  6.         goto end;  
  7.     }  
  8.   
  9.     //正常流程進入到這個函數  
  10.     sofia_reg_handle_register_token();  

 

我們從協議包反推,這裏應該在哪個地方判斷沒有認證,然後回覆401,所以可以搜索SIP_401_UNAUTHORIZED,這個宏,可以找到三處,然後進行log跟蹤,就可以知道在哪裏判斷未認證了。這裏應該是判斷有沒有authorization字段,調用sofia_reg_auth_challenge,在該函數回覆401。

這就是註冊的第一階段,收到register消息後,創建session,判斷未認證,回覆401。

  1. 回覆01 unauthorized
    1. 401信令

 

status-code:服務器回覆401表示未認證

WWW-Authenticate:服務端會生成認證摘要,一起發送給UA。

    1. 應用層mod_sofia

 

  1. void sofia_reg_auth_challenge(sofia_profile_t *profile, nua_handle_t *nh, sofia_dispatch_event_t *de,  
  2.                               sofia_regtype_t regtype, const char *realm, int stale, long exptime)  
  3. {  
  4.     switch_uuid_t uuid;  
  5.     char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];  
  6.     char *sql, *auth_str;  
  7.     msg_t *msg = NULL;  
  8.   
  9.     sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);  
  10.   
  11.     auth_str = switch_mprintf("Digest realm=\"%q\", nonce=\"%q\",%s algorithm=MD5, qop=\"auth\"", realm, uuid_str, stale ? " stale=true," : "");  
  12.   
  13.     if (regtype == REG_REGISTER) {  
  14.         nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END());  
  15.     } else if (regtype == REG_INVITE) {  
  16.         nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED,   
  17.                     TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)),   
  18.                     SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END());  
  19.     }  
  20.   
  21.     switch_safe_free(auth_str);  

 

這裏除了回覆401給客戶端UA,還要插入數據表sip_authentication。

    1. 協議棧

  1. 帶認證register
    1. 協議

 

UA收到401後,重新發起註冊請求,並帶上根據摘要和用戶名、密碼生成的認證信息。

 

    1. mod_sofia
  1. sofia_reg_handle_register_token()  
  2. {  
  3.     authorization = sip->sip_authorization;  
  4.   
  5.     if (authorization) {  
  6.         char *v_contact_str = NULL;  
  7.         const char *username = "unknown";  
  8.         const char *realm = reg_host;  
  9.         if ((auth_res = sofia_reg_parse_auth(profile, authorization, sip, de, sip->sip_request->rq_method_name,  
  10.                                              key, keylen, network_ip, network_port, v_event, exptime, regtype, to_user, &auth_params, ®_count, user_xml)) == AUTH_STALE) {  
  11.             stale = 1;  
  12.   
  13.         if (auth_res != AUTH_OK && auth_res != AUTH_RENEWED && !stale) {  
  14.             if (auth_res == AUTH_FORBIDDEN) {  
  15.                 nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());  
  16.                 forbidden = 1;  
  17.             } else {  
  18.                 nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());  
  19.             }  
  20.         }  
  21.   
  22.         nua_respond(nh, SIP_200_OK...)  

 

收到帶認證的register消息,流程大體一樣,只是到了這裏對認證進行校驗。認證成功後回覆200OK。如果認證不通過,則回覆403 forbidden。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章