1. 程式人生 > >基於OpenSIPS 實現分機註冊伺服器【非叢集】

基於OpenSIPS 實現分機註冊伺服器【非叢集】

      呼叫中心平臺中坐席是不可或缺的一環,而坐席打電話自然需要使用辦公分機。通常情況下我們通過軟交換平臺FreeSWITCH、Asterisk即可搭建分機註冊服務。       但單臺FreeSWITCH或Asterisk難以承載高併發的註冊服務,而且從服務模組化的角度,我們也希望將註冊服務和媒體服務相分離,所以我們通常會是使用OpenSIPS 或 Kamailio 來搭建註冊伺服器。       今天就讓我們一同來看一下,如何通過OpenSIPS搭建一個簡單的分機註冊伺服器吧……   目錄:
  1. 業務場景
  2. 執行環境
  3. 關鍵模組
  4. 涉及的資料庫表
  5. 分機註冊信令細節
  6. 註冊的認證過程
  7. auth_db模組變數說明
  8. 個性化功能
    • 禁止單個分機賬戶多地註冊
  9. 註冊腳步詳情
  10. 術語解釋
  11. 測試方法
  1. 業務場景:

    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: 0
View Code

 

6. 註冊的認證過程:         SIP 是採用HTTP摘要方式(Digest)進行認證。
  1. 認證:
    • 呼叫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),進行下一步
  2. 儲存註冊資訊:
    • 呼叫registrar模組的save("location", "f") 將註冊資訊寫入location表
    • 寫入DB失敗,則呼叫 sl 模組的 sl_reply_error() 返回錯誤資訊
    www_authorize(realm, table_name) :          realm : 所註冊分機所屬的域,通常是域名或IP
  • 該值並不一定是subscriber中的domain欄位,具體根據auth_db裡的引數配置決定。 (註冊伺服器返回401時,一定會有該引數,在所有的盤問中都必須有。它的目的是鑑別SIP訊息中的機密。在SIP實際應用中,它通常設定為SIP代理伺服器所負責的域名)
  • 通常該值是REG伺服器的IP,如果realm填空串"",對於REGISTER註冊請求而言,會從To頭取domain/ip地址(相當於$td), 而其他請求則從From頭中取(相當於$fd)。
  • 該值也可以使用偽變數指定。如 $rd  、$td 、$fd
        table_name : 認證資訊儲存的表名稱,如subscriber       www_challenge(realm, qop_enable) :         realm : 同上所屬         qop_enable : 
  • 是否雙向認證。可選值 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認證】
     除了身份認證,OpenSIPS還可以使用下面幾個方法完成代理認證:
  • proxy_authorize(realm, table_name)
  • proxy_challenge(realm, qop_enable)    [返回407,訊息頭 Proxy-Authenticate、Proxy-Authorization ]
  • consume_credentials()
  7. auth_db模組變數說明:
  • 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 
    subscriber表樣例:                  username : 使用者名稱(分機號)         domain     : 租戶名稱[IP、域名、或者是自己隨意定義的名稱],當你想支援多租戶配置時有用(modparam("auth_db", "use_domain", 1)),註冊請求From頭樣例: "9999"<sip:[email protected]>                從上面樣例中可以看到 分機號為 9999  的 ha1 和 ha1b 的值為空,因為我採用根據明文密碼計算MD5 hash值得方式模組引數password_column="password"、calculate_ha1=1   8. 個性化功能:      1、不允許單個分機賬號被擁有不同IP的多個終端同時註冊(但同一IP下,單個賬戶可以同時用多個SIP終端註冊),並且單個終端註冊成功後,可以重發註冊請求,以便更新過期時間     有以下兩種方案,其中方案A效能更好。  方案B 更靈活。        實現方案A:【從OpenSIPS記憶體中獲取分機登入狀態】
  • 通過 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地址。
  11. 測試方法:    啟動OpenSIPS :          /usr/local/OpenSIPS/sbin/OpenSIPS -f /usr/local/OpenSIPS/etc/OpenSIPS/OpenSIPS.cfg -P /var/run/opensips.pid -m 4096 -M 384 -u root -g root    可以通過 /usr/local/OpenSIPS/sbin/OpenSIPSctl ul show [分機號(可選)] 檢視記憶體中的分機的註冊狀態
[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