## 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`**

### Functions in `pjsip_simple/publishc.c`
#### ETag
- **`create_request()`**

- Add **SIP-If-Match**
- 
- **`tsx_callback()`**
- Save **ETag**
- 
- Add **SIP-If-Match**
- 
#### 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
```