基於OpenSIPS 實現分機註冊伺服器【非叢集】
阿新 • • 發佈:2020-08-16
呼叫中心平臺中坐席是不可或缺的一環,而坐席打電話自然需要使用辦公分機。通常情況下我們通過軟交換平臺FreeSWITCH、Asterisk即可搭建分機註冊服務。
但單臺FreeSWITCH或Asterisk難以承載高併發的註冊服務,而且從服務模組化的角度,我們也希望將註冊服務和媒體服務相分離,所以我們通常會是使用OpenSIPS 或 Kamailio 來搭建註冊伺服器。
今天就讓我們一同來看一下,如何通過OpenSIPS搭建一個簡單的分機註冊伺服器吧……
目錄:
- 業務場景
- 執行環境
- 關鍵模組
- 涉及的資料庫表
- 分機註冊信令細節
- 註冊的認證過程
- auth_db模組變數說明
- 個性化功能
- 禁止單個分機賬戶多地註冊
- 註冊腳步詳情
- 術語解釋
- 測試方法
OpenSIPS為分機提供註冊服務,分機可以經過OpenSIPS進行互打
2. 執行環境: CentOS 7.4 OpenSIPS 2.4.2 3. 使用的關鍵模組: SIP signaling modules : registrar、signaling、sl Auth modules : auth、auth_db Data caching : usrloc 4. 涉及的資料庫表: subscriber : 存放分機號、密碼等資訊 location : 已註冊的分機資訊 5. 分機註冊信令細節(RFC3261):- 分機註冊、取消註冊都是使用 SIP REGISTER 方法,只是取消註冊的時候,訊息頭中的過期時長expires的值是 0
- 分機註冊需要進行認證
- 註冊伺服器返回 401/407 來要求終端發起認證
- 認證不通過,則註冊伺服器返回 403 (Forbidden)
- 認證過程中,註冊伺服器找不到AOR (Address-of-Record),則返回404 (Not Found)
- 分機註冊的(建議)過期時長可以依次從下面兩個地方獲取 :
- Contact 頭中的 expires 引數
- Expires 頭
- 以上兩項都沒有,則由註冊伺服器指定一個預設值 (如OpenSIPS的registrar模組配置"default_expires=120)
- 註冊成功後的實際過期時長是由註冊伺服器來決定,並在註冊伺服器返回的200 OK中的Contact裡通過攜帶 'expires'引數來告知分機終端註冊資訊實際的有效時長:
- 終端REGISTER請求中的expires可能不在註冊伺服器允許expires範圍內,註冊伺服器會強制使用自己指定的expires值
- 比如REGISTER中的Expires=20, 而OpenSIPS配置的min_expires=30,那麼OpenSIPS返回的200 OK中的expires值是30 (如:Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf>;expires=30)
- 分機終端需要週期性傳送保活註冊包
- 保活過程中,每次重發註冊請求,CSeq 值自增 1
- 保活註冊時,Call-ID始終不變
- 保活註冊包傳送週期:在達到註冊伺服器返回的200 OK中的expires時長之前,重發保活註冊請求 (如Yealink會在expires/2時長後重新註冊,而Zoiper、EyeBeam會在註冊過期前5秒重新註冊)
- 如果註冊的響應報文包含Date 訊息頭,那麼SIP終端需要通過該值來保持跟註冊伺服器的時間同步,以確保註冊週期的準確性
1 下面報文中,401999 2 ===> 首次發起註冊 3 REGISTER sip:10.2.84.19 SIP/2.0 4 Via: SIP/2.0/UDP 10.32.26.19:56862;branch=z9hG4bK-d87543-c66bc353296ef71c-1--d87543-;rport 5 Max-Forwards: 70 6 Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf> 7 To: "401999"<sip:[email protected]> 8 From: "401999"<sip:[email protected]>;tag=704c1b46 9 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 10 CSeq: 1 REGISTER 11 Expires: 20 過期時間為 20秒 12 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO 13 User-Agent: eyeBeam release 1011d stamp 40820 14 Content-Length: 0 15 16 ===>未認證,要求認證,攜帶附加資訊WWW-Authenticate: 17 SIP/2.0 401 Unauthorized 18 Via: SIP/2.0/UDP 10.32.26.19:56862;received=10.32.26.19;branch=z9hG4bK-d87543-c66bc353296ef71c-1--d87543-;rport=56862 19 To: "401999"<sip:[email protected]>;tag=0903184bf06bf229be5b2f19c45648e4.a5a8 20 From: "401999"<sip:[email protected]>;tag=704c1b46 21 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 22 CSeq: 1 REGISTER 【CSeq 跟前一個一樣】 23 WWW-Authenticate: Digest realm="10.2.84.19", nonce="5eb6117930f436d5c6dfab18f8b2da91e65c1537" 24 Server: OpenSIPS (2.4.2 (x86_64/linux)) 25 Content-Length: 0 26 27 ===> 攜帶認證資訊(Authorization)後,再次重發註冊訊息 28 REGISTER sip:10.2.84.19 SIP/2.0 29 Via: SIP/2.0/UDP 10.32.26.19:56862;branch=z9hG4bK-d87543-f26ee314e330e316-1--d87543-;rport 30 Max-Forwards: 70 31 Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf> 32 To: "401999"<sip:[email protected]> 33 From: "401999"<sip:[email protected]>;tag=704c1b46 34 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 35 CSeq: 2 REGISTER 【CSeq 跟前一個一樣】 36 Expires: 20 37 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO 38 User-Agent: eyeBeam release 1011d stamp 40820 39 Authorization: Digest username="401999",realm="10.2.84.19",nonce="5eb6117930f436d5c6dfab18f8b2da91e65c1537",uri="sip:10.2.84.19",response="9c46fa8d95df62b2d204c82bf4fcdccc",algorithm=MD5 40 Content-Length: 0 41 42 ===> 首次註冊成功 43 你可能注意到,200 OK 中 Contact 裡的 expires=30, 不是終端請求是設定的 20 44 因為我OpenSIPS的registrar模組配置的最小過期時長是30 45 ==== 46 SIP/2.0 200 OK 47 Via: SIP/2.0/UDP 10.32.26.19:56862;received=10.32.26.19;branch=z9hG4bK-d87543-f26ee314e330e316-1--d87543-;rport=56862 48 To: "401999"<sip:[email protected]>;tag=0903184bf06bf229be5b2f19c45648e4.fdb8 49 From: "401999"<sip:[email protected]>;tag=704c1b46 50 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 51 CSeq: 2 REGISTER 52 Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf>;expires=30 53 Server: OpenSIPS (2.4.2 (x86_64/linux)) 54 Content-Length: 0 55 56 ===> 15秒後再次發起註冊【因為SIP終端是 EyeBeam,它是在過期前5秒重新發起註冊】,注意,這次重發時,沒有返回401 57 REGISTER sip:10.2.84.19 SIP/2.0 58 Via: SIP/2.0/UDP 10.32.26.19:56862;branch=z9hG4bK-d87543-83347a2a0b242e5c-1--d87543-;rport 59 Max-Forwards: 70 60 Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf> 61 To: "401999"<sip:[email protected]> 62 From: "401999"<sip:[email protected]>;tag=704c1b46 63 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 64 CSeq: 3 REGISTER 【CSeq 跟前一個一樣】 65 Expires: 20 66 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO 67 User-Agent: eyeBeam release 1011d stamp 40820 68 Authorization: Digest username="401999",realm="10.2.84.19",nonce="5eb6117930f436d5c6dfab18f8b2da91e65c1537",uri="sip:10.2.84.19",response="9c46fa8d95df62b2d204c82bf4fcdccc",algorithm=MD5 69 Content-Length: 0 70 71 SIP/2.0 200 OK 72 Via: SIP/2.0/UDP 10.32.26.19:56862;received=10.32.26.19;branch=z9hG4bK-d87543-83347a2a0b242e5c-1--d87543-;rport=56862 73 To: "401999"<sip:[email protected]>;tag=0903184bf06bf229be5b2f19c45648e4.f41b 74 From: "401999"<sip:[email protected]>;tag=704c1b46 75 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 76 CSeq: 3 REGISTER 77 Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf>;expires=30 78 Server: OpenSIPS (2.4.2 (x86_64/linux)) 79 Content-Length: 0 80 81 ===> 15秒後再次發起註冊,注意,這次重發時,返回401,要求採用最新的 nonce值認證 82 REGISTER sip:10.2.84.19 SIP/2.0 83 Via: SIP/2.0/UDP 10.32.26.19:56862;branch=z9hG4bK-d87543-1e206865de74ec34-1--d87543-;rport 84 Max-Forwards: 70 85 Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf> 86 To: "401999"<sip:[email protected]> 87 From: "401999"<sip:[email protected]>;tag=704c1b46 88 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 89 CSeq: 4 REGISTER 90 Expires: 20 91 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO 92 User-Agent: eyeBeam release 1011d stamp 40820 93 Authorization: Digest username="401999",realm="10.2.84.19",nonce="5eb6117930f436d5c6dfab18f8b2da91e65c1537",uri="sip:10.2.84.19",response="9c46fa8d95df62b2d204c82bf4fcdccc",algorithm=MD5 94 Content-Length: 0 95 96 ===> 返回401,WWW-Authenticate 中 nonce 的值發生改變 97 SIP/2.0 401 Unauthorized 98 Via: SIP/2.0/UDP 10.32.26.19:56862;received=10.32.26.19;branch=z9hG4bK-d87543-1e206865de74ec34-1--d87543-;rport=56862 99 To: "401999"<sip:[email protected]>;tag=0903184bf06bf229be5b2f19c45648e4.79d6 100 From: "401999"<sip:[email protected]>;tag=704c1b46 101 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 102 CSeq: 4 REGISTER 103 WWW-Authenticate: Digest realm="10.2.84.19", nonce="5eb611acb7d0e927969146dcaf0ce1777070df6e", stale=true 104 Server: OpenSIPS (2.4.2 (x86_64/linux)) 105 Content-Length: 0 106 107 ===> 使用新的 nonce 值再次發起註冊,並且返回成功 108 REGISTER sip:10.2.84.19 SIP/2.0 109 Via: SIP/2.0/UDP 10.32.26.19:56862;branch=z9hG4bK-d87543-7f61e57ce525c45d-1--d87543-;rport 110 Max-Forwards: 70 111 Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf> 112 To: "401999"<sip:[email protected]> 113 From: "401999"<sip:[email protected]>;tag=704c1b46 114 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 115 CSeq: 5 REGISTER 116 Expires: 20 117 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO 118 User-Agent: eyeBeam release 1011d stamp 40820 119 Authorization: Digest username="401999",realm="10.2.84.19",nonce="5eb611acb7d0e927969146dcaf0ce1777070df6e",uri="sip:10.2.84.19",response="1f1a4b29257e292a5efc686ea846245d",algorithm=MD5 120 Content-Length: 0 121 122 SIP/2.0 200 OK 123 Via: SIP/2.0/UDP 10.32.26.19:56862;received=10.32.26.19;branch=z9hG4bK-d87543-7f61e57ce525c45d-1--d87543-;rport=56862 124 To: "401999"<sip:[email protected]>;tag=0903184bf06bf229be5b2f19c45648e4.771a 125 From: "401999"<sip:[email protected]>;tag=704c1b46 126 Call-ID: YjEwZDFhZDJmY2Y0MDg4ZDQ1YjJhMTU0MGFlYTE0YmQ. 127 CSeq: 5 REGISTER 128 Contact: <sip:[email protected]:56862;rinstance=870ce245f5361eaf>;expires=30 129 Server: OpenSIPS (2.4.2 (x86_64/linux)) 130 Content-Length: 0 131 132 ===> 401998 取消註冊的SIP報文 133 REGISTER sip:10.2.84.19:5060 SIP/2.0 134 Via: SIP/2.0/UDP 10.34.26.140:5060;branch=z9hG4bK3803710069 135 From: "401998" <sip:[email protected]:5060>;tag=2259286663 136 To: "401998" <sip:[email protected]:5060> 137 Call-ID: [email protected] 138 CSeq: 3 REGISTER 139 Contact: <sip:[email protected]:5060> 140 Authorization: Digest username="401998", realm="10.2.84.19", nonce="5eb503f9b40c6d1f396eef36c55a6713b8421c74", uri="sip:10.2.84.19:5060", response="c5e2ee46cba287df8f434fe631a2f3fd", algorithm=MD5 141 Allow: INVITE, INFO, PRACK, ACK, BYE, CANCEL, OPTIONS, NOTIFY, REGISTER, SUBSCRIBE, REFER, PUBLISH, UPDATE, MESSAGE 142 Max-Forwards: 70 143 User-Agent: Yealink SIP-T21P_E2 52.80.0.147 144 Expires: 0 145 Allow-Events: talk,hold,conference,refer,check-sync 146 Content-Length: 0View Code
6. 註冊的認證過程: SIP 是採用HTTP摘要方式(Digest)進行認證。
- 認證:
- 呼叫auth_db模組的www_authorize("", "subscriber")進行分機身份認證,根據認證情況繼續
- 返回 -3 (stale nonce)、-4 (no credentials),則呼叫 auth 模組的 www_challenge("","0")要求分機攜帶認證憑證再次註冊。該方法會返回SIP 401,並且訊息頭中有一個 WWW-Authenticate 頭,等SIP終端再次傳送註冊請求時就會在SIP INVITE 內攜帶 Authorization 頭資訊
- 返回 -1 (invalid user),則呼叫signaling模組send_reply("404", "Not Found")
- 返回 -2 (invalid password)、-5 (generic error),則呼叫signaling模組send_reply("403", "Forbidden")
- 返回 1 (success),進行下一步
- 儲存註冊資訊:
- 呼叫registrar模組的save("location", "f") 將註冊資訊寫入location表
- 寫入DB失敗,則呼叫 sl 模組的 sl_reply_error() 返回錯誤資訊
- 該值並不一定是subscriber中的domain欄位,具體根據auth_db裡的引數配置決定。 (註冊伺服器返回401時,一定會有該引數,在所有的盤問中都必須有。它的目的是鑑別SIP訊息中的機密。在SIP實際應用中,它通常設定為SIP代理伺服器所負責的域名)
- 通常該值是REG伺服器的IP,如果realm填空串"",對於REGISTER註冊請求而言,會從To頭取domain/ip地址(相當於$td), 而其他請求則從From頭中取(相當於$fd)。
- 該值也可以使用偽變數指定。如 $rd 、$td 、$fd
- 是否雙向認證。可選值 0、1, 當qop_enable=1時,後續步驟中的 WWW-Authenticate 和 Authorization 步驟得值都會攜帶qop引數,401會有nonce, 再次傳送的 REGISTER會有cnonce
- 如:qop_enable=1時,返回401訊息:WWW-Authenticate: Digest realm="10.2.32.112", nonce="5f05c370000000090661a6e56d5451e0ba81b4883dc37bca", qop="auth", stale=true
- 再次傳送的REGISTER訊息:Authorization: Digest username="9001",realm="10.2.32.112",nonce="5f05c370000000090661a6e56d5451e0ba81b4883dc37bca",uri="sip:10.2.32.112;transport=tcp",response="d5792a2d89b0c158c1c9453f98385cb8",cnonce="06681fd4664cda35938959789ed84849",nc=00000001,qop=auth,algorithm=MD5【response是加密後的密碼, 其他引數的解釋請參見https://www.cnblogs.com/shengs/p/4361314.html 或RFC2617 HTTP認證】
- proxy_authorize(realm, table_name)
- proxy_challenge(realm, qop_enable) [返回407,訊息頭 Proxy-Authenticate、Proxy-Authorization ]
- consume_credentials()
- db_url : (string) 資料庫訪問路徑,如果不設定,則採用db_default_url
- user_column : (string) 資料庫subscriber表中用於存放UA的使用者名稱資訊的列名,預設是username
- domain_column : (string) 資料庫subscriber表中存放UA所屬的租戶資訊的列名,預設是domain
- password_column : (string) OpenSIPS執行時,使用subscriber表中作為UA密碼的列名,預設是 ha1
- 根據username、password、realm(非表中的domain)計算出的 MD5 hash值
- 使用 MI命令 opensipsctl add username password 會自動生成 ha1的值
- 注意,如果calculate_ha1=1,必須設定password_column="password",用於儲存明文密碼;calculate_ha1=0 時,該值可不設或者設定成ha1
- password_column_2 : (string) OpenSIPS執行時,使用subscriber表中作為UA密碼的列名,預設是 ha1b
- 同ha1, 但計算hash的方式不同,根據 username@domain、password、realm 計算出的MD5 hash值
- 只有當calculate_ha1=0,並且UA發起的註冊請求中username攜帶domain資訊時有用,如[email protected] [$fu=sip:[email protected]@10.2.32.112;transport=UDP]
- calculate_ha1 : (int) OpenSIPS處理REGISTER請求時,是否需要重新計算密碼的HASH值,取值分別如下:
- 1 :OpenSIPS會從載入明文密碼,然後計算MD5 hash值,所以password_column的值必須=password
- 0 :(預設值)不計算密碼,如果註冊請求中username不帶domain時,則直接從ha1中取密碼進行驗證,否則取ha1b的值【此時不能設定成password_column="password"】
- user_domain : (int) 是否支援多租戶,取值範圍 [0,1] ,預設值 0。
- MI命令 opensipsctl add username password 不支援新增多租戶的UA資訊
- 所以需要自己想辦法為不同租戶初始化subscriber表中的 ha1和ha1b,當然你不設定ha1和ha1b,OpenSIPS根據明文計算的方式解決(模組引數password_column="password"、calculate_ha1=1
- 通過 registar 模組的 is_ip_registered、is_registered方法檢測當前註冊分機是否已經被註冊過,如果已經被註冊,則直接返回返回403 , 提示賬戶被佔用 Occupied
- 如果同時滿足下面幾個條件,則不允許再註冊:(存在已經註冊的分機,但IP不是自己)
- (1) is_ip_registered 根據 $tu 分機號(username) 和 $si (分機終端IP) 檢測到分機未註冊 【未註冊:返回 -1】
- (2) is_registered 檢測到$tu 分機號(username) 已經註冊 【已註冊:返回 1】
- 其中第一步如果返回1,表示已註冊,則需放行因分機註冊資訊過期而再次發起的註冊請求,以便更新過期時間【因為根據終端IP判斷,所以不會影響端斷電重啟後再次註冊】
- [備註:registar 模組還有 is_contact_registered方法,可以根據分機號+callid判斷分機註冊情況,如 is_contact_registered("location", "$tu", , "$ci") ]
1 # 此處省略分機認證過程 2 $var(pA) = is_ip_registered("location", "$tu", "$si"); 3 # check current callid not registed 4 if ( $var(pA) < 0 ) { 5 xlog("SIP contact ct:[$ct] [$tu] ci:[$ci] did not registe on, then check registe status for tU:[$tU]."); 6 7 if ( is_registered("location") ) { # if current caller has registed by another UA 8 xlog("L_WARN", "Forbid $ct to registe on, cuase by : exist another $fU has registed"); 9 send_reply("403", "Occupied"); 10 exit; 11 } 12 } 13 14 if (!save("location", "f")) { #將註冊資訊寫入DB 15 sl_reply_error(); 16 }View Code 實現方案B:【從DB獲取分機登入狀態】
- 通過avpops模組的 avp_db_query 直接查詢資料庫的 location 表來實現功能
1 # 此處省略分機認證過程 2 avp_db_query("select count(*) from location where username = '$fU' and contact like 'sip:$fU@$si%'", "$avp(existExtenCount)"); 3 if ( $avp(existExtenCount) < 1 ) { 4 xlog("SIP contact ct:[$ct] [$tu] ci:[$ci] did not registe on, then check registe status for tU:[$tU]."); 5 6 avp_db_query("select count(*) from location where username = '$fU'", "$avp(ct4fU)"); 7 if ( $avp(ct4fU) > 0) { 8 xlog("L_WARN", "Forbid $ct to registe on, cuase by : exist another $fU has registed"); 9 send_reply("403", "Occupied"); 10 exit; 11 } 12 } 13 14 if (!save("location", "f")) { #將註冊資訊寫入DB 15 sl_reply_error(); 16 }View Code 9. 註冊腳步詳情:
- 通過下面腳步,註冊幾個分機後,就能完成分機互打了
- 新增分機的方式 : /usr/local/OpenSIPS/sbin/OpenSIPSctl add ${分機號} ${密碼}
- /usr/local/OpenSIPS/sbin/OpenSIPSctl add 9001 9001
- 新增的分機會寫入 subscriber 表, 註冊資訊會寫入 location表
1 log_level=3 2 log_stderror=no 3 log_facility=LOG_LOCAL0 4 5 children=4 6 auto_aliases=no 7 8 listen=udp:192.168.1.201:5060 # CUSTOMIZE ME 9 10 ####### Modules Section ######## 11 12 mpath="/usr/local/OpenSIPS242//lib64/OpenSIPS/modules/" 13 14 db_default_url="mysql://root:[email protected]:3306/OpenSIPS242" 15 16 loadmodule "proto_udp.so" 17 loadmodule "db_mysql.so" 18 loadmodule "signaling.so" 19 loadmodule "sl.so" 20 loadmodule "maxfwd.so" 21 loadmodule "sipmsgops.so" 22 23 loadmodule "rr.so" 24 modparam("rr", "append_fromtag", 0) 25 26 loadmodule "uri.so" 27 modparam("uri", "use_uri_table", 0) 28 29 loadmodule "mi_fifo.so" 30 modparam("mi_fifo", "fifo_name", "/tmp/OpenSIPS_fifo") 31 modparam("mi_fifo", "fifo_mode", 0666) 32 33 loadmodule "tm.so" 34 modparam("tm", "fr_timeout", 5) 35 modparam("tm", "fr_inv_timeout", 30) 36 modparam("tm", "restart_fr_on_each_reply", 0) 37 modparam("tm", "onreply_avp_mode", 1) 38 39 loadmodule "auth.so" 40 loadmodule "auth_db.so" 41 #需要根據明文密碼重新計算MD5 hash值 42 modparam("auth_db", "calculate_ha1", 1) 43 #是否支援多租戶:subscriber表中支援 username相同,但domain欄位不同。註冊時,會查domain 44 modparam("auth_db", "use_domain", 1) 45 #從明文密碼列取密碼 46 modparam("auth_db", "password_column", "password") 47 48 loadmodule "usrloc.so" 49 modparam("usrloc", "db_mode", 1) 50 modparam("usrloc", "nat_bflag", "NAT_BFLAG") 51 #modparam("usrloc", "working_mode_preset", "single-instance-sql-write-through") 52 #是否支援多租戶 53 modparam("usrloc", "use_domain", 1) 54 modparam("usrloc", "hash_size", 16) 55 56 #### REGISTRAR module 57 loadmodule "registrar.so" 58 modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT") 59 modparam("registrar", "max_contacts", 1) 60 modparam("registrar", "min_expires", 30) 61 modparam("registrar", "max_expires", 300) 62 modparam("registrar", "default_expires", 120) 63 64 ####### Routing Logic ######## 65 66 # main request routing logic 67 68 route{ 69 70 xlog("receive message method[$rm] fU[$fU] tU[$tU] | ci[$ci] si[$si] sp[$sp] rd[$rd] rU[$rU] tu[$tu] fd[$fd] ct[$ct]"); 71 72 if (!mf_process_maxfwd_header("10")) { 73 send_reply("483","Too Many Hops"); 74 exit; 75 } 76 77 if (is_method("OPTIONS")){ 78 t_reply("200", "OK"); 79 exit; 80 } 81 82 $var(pTg) = has_totag(); 83 xlog("method[$rm] has_totag = $var(pTg)"); 84 85 if ( $var(pTg) > 0) { 86 87 if (loose_route()) { 88 xlog("loose_route ---> as receive message method[$rm]"); 89 # route it out to whatever destination was set by loose_route() in $du (destination URI). 90 route(relay); 91 } else { 92 if ( is_method("ACK") ) { 93 if ( t_check_trans() ) { 94 # non loose-route, but stateful ACK; must be an ACK after a 487 or e.g. 404 from upstream server 95 xlog("receive message method[$rm] then do t_relay"); 96 t_relay(); 97 exit; 98 } else { 99 xlog("receive message method[$rm] no transaction , then exit"); 100 exit; 101 } 102 } 103 sl_send_reply("404","Not here"); 104 } 105 exit; 106 } 107 108 # CANCEL processing 109 if (is_method("CANCEL")) { 110 if (t_check_trans()) 111 t_relay(); 112 exit; 113 } 114 115 # absorb retransmissions, but do not create transaction 116 t_check_trans(); 117 118 # record routing 119 if (!is_method("REGISTER|MESSAGE")) { 120 record_route(); 121 } 122 123 # requests for my domain 124 if (is_method("PUBLISH|SUBSCRIBE")) { 125 t_reply("405", "Method Not Allowed "); 126 exit; 127 } 128 129 if (is_method("REGISTER")) { 130 xlog("rhjiang---test--Do REGISTER AUTH : [$rm] ci[$ci] si[$si] sp[$sp] rd[$rd] rU[$rU] fU[$fU]"); 131 route(AUTH); 132 } 133 134 if ($rU==NULL) { 135 # request with no Username in RURI 136 send_reply("484","Address Incomplete"); 137 exit; 138 } 139 140 # do lookup with method filtering 141 if (!lookup("location","m")) { 142 t_reply("404", "Not Found"); 143 exit; 144 } 145 146 route(relay); 147 } 148 149 150 route[relay] { 151 # for INVITEs enable some additional helper routes 152 if (is_method("INVITE")) { 153 t_on_branch("per_branch_ops"); 154 t_on_reply("handle_reply"); 155 t_on_failure("missed_call"); 156 } 157 158 if (!t_relay()) { 159 send_reply("500","Internal Error"); 160 } 161 exit; 162 } 163 164 165 branch_route[per_branch_ops] { 166 xlog("new branch at $ru\n"); 167 } 168 169 170 onreply_route[handle_reply] { 171 xlog("incoming reply [$ci] [$si:$sp] [$rs]\n"); 172 } 173 174 failure_route[missed_call] { 175 if (t_was_cancelled()) { 176 exit; 177 } 178 } 179 180 181 route[AUTH]{ 182 183 $var(authRslt) = www_authorize("$td", "subscriber"); # the same as --> www_authorize("", "subscriber"); 184 xlog("register auth result [$var(authRslt)] rd [$rd] user[$fU] source ip[$si]"); 185 switch ($var(authRslt)) { 186 case -1: 187 send_reply("404", "Not Found"); 188 exit; 189 case -2: 190 case -5: 191 send_reply("403", "Forbidden"); 192 exit; 193 case -3: 194 case -4: 195 ###www_challenge("$td","1"); 196 www_challenge("$td","0"); 197 exit; 198 } 199 200 $var(pA) = is_ip_registered("location", "$tu", "$si"); 201 if ( $var(pA) < 0 ) { 202 xlog("====SIP contact ct:[$ct] [$fu] ci:[$ci] did not registe on, then check registe status for fU:[$fU]."); 203 204 if ( is_registered("location")) { 205 xlog("L_WARN", "====Forbid $ct to registe on, cuase by : exist another $fU has registed"); 206 send_reply("403", "Occupied"); 207 exit; 208 } 209 } 210 211 if (!save("location", "f")) { 212 sl_reply_error(); 213 } 214 215 $var(expire) = "0"; 216 if ( $hdrcnt(Expires) == 0 ) { 217 $var(expire) = $ct.fields(expires); 218 xlog("no expire header ,contact expire is [$var(expire)]"); 219 } else { 220 $var(expire) = $hdr(Expires); 221 xlog("expires header , expire [$var(expire)]"); 222 } 223 224 exit; 225 }View Code 10. 術語解釋:
- Contact : SIP終端賬戶的具體物理IP和埠,用於描述我在哪裡。一個註冊請求中可以有多個Contact訊息頭。SIP終端之間可以通過Contact中的資訊實現點對點通訊。
- AOR (Address of Record) : SIP終端賬戶的唯一標識,用於描述我是誰,From 和 To 頭中就是 AOR地址,格式為 SIP:username@domain_host 或者 SIP:username@ip 。 AOR地址是可以解析得到Contact地址。
[root@yuxiu home]# /usr/local/OpenSIPS/sbin/OpenSIPSctl ul show 9001 AOR:: 9001 Contact:: sip:[email protected]:5060 Q= ContactID:: 1767885864954038130 Expires:: 163 Callid:: [email protected] Cseq:: 96 User-agent:: SIP-T21P_E2 52.80.0.147 State:: CS_SYNC Flags:: 0 Cflags:: Socket:: udp:192.168.1.218:5060 Methods:: 16383
相關網址:
https://tools.ietf.org/html/rfc3261#section-10 https://www.opensips.org/Documentation/Tutorials-MidRegistrar http://www.kamailio.org.cn/doku.php?id=OpenSIPS_cfg_mysql%E5%88%86%E6%9C%BA%E6%B3%A8%E5%86%8C 【分機註冊】 https://mjd507.github.io/2018/01/25/HTTP-Authorization/ 【HTTP認證方式】
&n