## Entity-Tag - 接下來要詳細介紹一下 PUBLISH 會使用到的 Entity-Tag,但在這之前讓我們先來看一下 Entity-Tag 在 HTTP 中的應用。 ### HTTP ETag - 以下的內容是參考自 RFC 2616,這篇 RFC 是 HTTP/1.1 版的標準文件,而 Entity-Tag 被寫在 section 3.11。這邊我就簡單介紹一下: - HTTP Entity-Tag 被用來辨別不同的 URI 發送的 request, request 的內容就是我們所謂的 entity. - Entity-Tag 是由不透明且帶有引號的字串組成,通常字串前面會再加上一個辨識用的標示符,被稱為 weakness indicator。 - Entity-Tag 具有唯一性,只要該 entity 有任何的更動,都會再產生一個新的 Entity-Tag 並把把舊的淘汰,Server 在收到 Client 送來的 request 後會檢查裡面附帶的 Entity-Tag 是否跟自己這邊紀錄的相符,是的話才會處理那個 request。 - 這有點像是用 GitHub 做版本控制時,新的 commit 會被 assign 新的 ID number 一樣。但不同的是 Entity-Tag 沒辦法用來回朔過去的狀態,只有最新產生的 Entity-Tag 才有用處。 ### PUBLISH Entity-Tag - PUBLISH Entity-Tag 就是參考 HTTP Entity-Tag 去延伸設計的。 - 你們會發現 SIP 有很多地方都跟 HTTP 很相似: request method, response code, entity-tag, authentication. - 在 SIP PUBLISH 中 ESC(SIP Server) 用 Entity-Tag 來辨別不同的 URI 發送的 PUBLISH request,在這裡所謂的 entity 就是 PUBLISH request 中的 event state。 - SIP PUBLISH 在概念上跟 HTTP PUT 類似,但 Entity-Tag 的應用還是有幾點不同: - 剛剛前一頁有提到 HTTP Entity-Tag 是由不透明且帶有引號的字串組成,但在 SIP PUBLISH 中 Entity-Tag 是由 token 的形式存在,而不是一個帶引號的字串。 - 再來 3903 裡面還提到 PUBLISH Request 一次只能附帶一個 Entity-Tag,我們沒辦法同時要求對多個 publication 進行操作。 - 在 HTTP 中 Entity-Tag 是 optional 的,管理者可以選擇不使用。但在 SIP 只要 ESC 成功受理了一個 PUBLISH Request 就一定要回一個帶有 Entity-Tag 的 Response。 ### Client/Server Usage 雖然宇騰前面有提過 PUBLISH 的流程,我在這邊在強調一下 Entity-Tag 分別在 EPA 跟 ESC 的作用。 - EPA 想 Initial 一個新的 publication 時會發送不帶有 Entity-Tag 的 PUBLISH Request 給 ESC,並從 ESC 那裡取得一個 Entity-Tag。 - EPA 要自己存好這個 Entity-Tag。當 EPA 在任何後續發送的 PUBLISH request 中要去修改、刷新或刪除該 event state 時都會使用 Entity-Tag 讓 ESC 進行辨識。 - 稍早我們有提到過,當 event state 有任何更動都會讓 ESC 再產生一個新的 Entity-Tag 給 EPA。所以基本上只要 EPA 再有新的 PUBLISH Request 送出,都應該要得到新的 Entity-Tag 並淘汰舊的。 - 關於 Entity-Tag 的 format 要如何制定以及所有產生的 Entity-Tag 要如何管理都是由 ESC 決定。 - ESC 只要成功受理一個 PUBLISH Request 就是要回一個帶有新 Entity-Tag 的 200 OK。 :::spoiler Event Packages - 並非所有的 event packages 都有 multiple sources 的需求, 若有需要則要在最初設計時就定義清楚。 - For presence, a PUA can publish presence state for just a subset of the tuples that may be composited into the presence document that watchers receive in a NOTIFY. - 關於 ESC 是否支援 event state 的 segmentation 也應該在最初設計時就定義清楚 ESC 處理 segments 的方式。 - In presence publication, the EPA MUST keep the "id" attributes of tuples consistent in the context of an entity-tag. ::: ### Protocol Elements - 為了實作 publication 的功能,就直接定義了一個新的 Method "PUBLISH"。 - 也為了 PUBLISH 這個新的 Method 定義了一個新的 response code 412。當 ESC 收到 PUBLISH Request,卻發現裡面附帶的 Entity-Tag 是無效的時候就會回覆一個帶有 code 412 的 response。 - 新欄位 ## Security Considerations HTTP 如何進行身分驗證 ### Basic - Client 會使用 user ID 跟 password 讓 Server 驗證自己的身分。 - 只有經過身分驗證後的 request 才會被 server 處理。 - user ID 跟 password 是以明文的形式去傳送。 ### Digest - user ID 跟 password 不是以明文的形式去傳送,通常會使用 hash function 去進行加密。 - 另外,每一個 request 也會使用 checksum 來確保資料的正確性。 兩者的差別就在身分驗證用的資料是否有在傳送前先被加密過。 ### ACL - 如果每個 client 都能作為 EPA 自由的向 ESC 發布 publication 是不安全的。 - ESC 應該要能根據 EPA 的身份 選擇性地接受對方發過來的 PUBLISH Request。 - 所以 ESC 通常要有個 access control list 去管理被授權的 EPA。 - 應用 Digest Authentication 保護 EPA 的身分驗證資料。 ### Replay ### Dos 謹慎選擇 default Expires Time,避免 Publication 過於頻繁的更新。 ## QA - 文中有提到entity-tag是在successful publish request時才會產生,那如果是失敗的,為什麼不用產生呢? --- ## Trace Code ### Structure - **`pjsip_publishc`** ![](https://hackmd.io/_uploads/SytRxC29h.png) ### Functions in `pjsip_simple/publishc.c` #### ETag - **`create_request()`** ![](https://hackmd.io/_uploads/BJn0gA3qh.png) - Add **SIP-If-Match** - ![](https://hackmd.io/_uploads/BJ9-Z02c2.png) - **`tsx_callback()`** - Save **ETag** - ![](https://hackmd.io/_uploads/SkBRU0353.png) - Add **SIP-If-Match** - ![](https://hackmd.io/_uploads/SJiQqR293.png) #### Expires - `set_expires()` - `pjsip_publishc_update_expires()` #### Publish - `pjsip_publishc_create()` - `pjsip_publishc_init()` - `pjsip_publishc_publish()` - `pjsip_publishc_send()` - ### Lab: ETag Changed ```c= /* Add SIP-If-Match if we have etag */ if (pubc->etag.slen) { const pj_str_t STR_HNAME = { "SIP-If-Match", 12 }; // Modify by Phoebe 7/25 pj_str_t myetag; char* myetag_ptr = (char*)malloc(256); printf("Input your partner's UA ETag: "); scanf("%s", myetag_ptr); myetag.ptr = myetag_ptr; myetag.slen = strlen(myetag_ptr); if(myetag.slen) hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME, &myetag); //hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME, &pubc->etag); // End of Modification if (hdr) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); } ``` ### Lab: Request URI Changed ```c= /* Add method. */ len = msg->line.req.method.name.slen; pj_memcpy(p, msg->line.req.method.name.ptr, len); // Modify by Phoebe 7/25 *(p + len) = '\0'; char * comp_str = "PUBLISH"; if (strcmp(p, comp_str) == 0) { p += len; *p++ = ' '; char* myuri_buf = "sip:111321511@sip.ncnu.net"; strcpy(p, myuri_buf); //*(p + strlen(myuri_buf)) = '\0'; //printf("Print out the Request URI: %s\n", p); p += strlen(myuri_buf); } // End of Modification else { p += len; *p++ = ' '; /* Add URI */ uri = (pjsip_uri*)pjsip_uri_get_uri(msg->line.req.uri); len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, p, end - p); if (len < 1) return -1; p += len; } ``` ### Command ```shell= pjsua.exe --id=sip:111321512@sip.ncnu.net --registrar=sip:sip.ncnu.net --no-tcp --publish --reg-timeout=3600 ```