2018年12月28日 星期五

RFC 5424 Syslog Message Format

廣為使用的 syslog 來自 BSD,訊息格式並沒有標準化,共通點只有都是以「<數字>」開始。RFC 3164 只是說明觀察到的格式,認定送到 syslog UDP port (514) 的封包都是 syslog 訊息。

RFC 5424ANBF 定義了格式:
SYSLOG-MSG      = HEADER SP STRUCTURED-DATA [SP MSG]
HEADER          = "<" PRIVAL ">" VERSION SP TIMESTAMP SP HOSTNAME
                  SP APP-NAME SP PROCID SP MSGID
STRUCTURED-DATA = "-" / 1*SD-ELEMENT
SD-ELEMENT      = "[" ID *(SP NAME "=" %d34 VALUE %d34) "]"

依序包括下列以空白區隔的欄位:

  • <PRIVAL>VERSIONPRIVAL 是優先值數字 = facility * 8 + severity。facility 值和 severity 值的表見下面。VERSION 是格式的版本,目前 = 1。
  • TIMESTAMP:格式「為西元年--T::.fraction時區」,小數點後可有 6 位精度達微秒,可有時區。如不知道時間填「-」。RFC 3164 的時間精度較差、沒「年」和「時區」。
  • HOSTNAME:可以是 FQDN、IP、主機名稱。在 RFC 3164 沒明確說明。
  • APP-NAME:應用程式名稱。RFC 3164 的 TAG 大致分成 APP-NAME、PROCID、和 MSGID。
  • PROCID
  • MSGID
  • STRUCTURED-DATA:可以是「-」或一個以上連續 [SD-ELEMENT] (之間沒空白)。RFC 3164 沒有這部份。
  • MSG:之後可選擇性加空白後放任意資訊 (MSG)。相當於 RFC 3164 的 CONTENT。
例如:
<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] BOMAn application event log entry...
可看到:
  • PRIVAL 165 = 20 * 8 + 5,所以 facility 值 = 20,severity 值 = 5。
  • VERSION = 1。
  • TIMESTAMP 小數點後有 3 位,精度毫秒。Z 代表???。
  • HOSTNAME 為 FQDN。
  • APP-NAME 為 evntslog。
  • PROCID 為 -
  • MSGID 為 ID47
  • 一個 SD-ELEMENT 由 [] 框起來,有一個 ID 和多個空白隔開的 NAME="VALUE" 參數。
    • ID 和 NAME 是 1 ~ 32 字元 PRINTUSASCII,不含 '=', SP, ']', '"'。
    • VALUE 是 utf-8 資料,但 '"', '\' 和 ']' 分別 escape 為 '\"', '\\', and '\]'。   Escape ']' 是為了避免 syslog 實作錯誤。'\' 之後不是這三個字元視為一般的 '\'。
    • 可有多個 SD-ELEMENT,但 ID 不能重複。ID 有兩類:
      • 不含「@」的 ID 要跟 IANA 註冊,目前有的見 Section 7。
      • 可自行定義的格式是 name@<private enterprise number>, e.g., "ourSDID@32473"。private enterprise number 見 Section 7.2.2。
  • MSG 任意訊息,開頭 BOM (實際上傳的是 utf-8 的 BOM 0xEF 0xBB 0xBF) 代表傳的是 utf-8 資料。如果不是 BOM 開頭則是其它編碼。小於 32 的字元是合法的,但可能被改變 (例如 0 可能被改為 "\0"),所以應該要避免。
數值Facility
0kernel messages
1user-level messages
2mail system
3system daemons
4security/authorization messages
5messages generated internally by syslogd
6line printer subsystem
7network news subsystem
8UUCP subsystem
9clock daemon
10security/authorization messages
11FTP daemon
12NTP subsystem
13log audit
14log alert
15clock daemon (note 2)
16local use 0 (local0)
17local use 1 (local1)
18local use 2 (local2)
19local use 3 (local3)
20local use 4 (local4)
21local use 5 (local5)
22local use 6 (local6)
23local use 7 (local7)
數值Severity
0Emergency: system is unusable
1Alert: action must be taken immediately
2Critical: critical conditions
3Error: error conditions
4Warning: warning conditions
5Notice: normal but significant condition
6Informational: informational messages
7Debug: debug-level messages

sect6.1 訊息長度 sect6.1
...

A.2.  Message Length

   Implementers should note the message size limitations outlined in
   Section 6.1 and try to keep the most important data early in the
   message (within the minimum guaranteed length).  This ensures the
   data will be seen by the collector or relay even if a transport
   receiver at a relay on the message path truncates the message.

   The reason syslog transport receivers need only support receiving up
   to and including 480 octets has, among other things, to do with
   difficult delivery problems in a broken network.  Syslog messages may
   use a UDP transport mapping with this 480 octet restriction to avoid
   session overhead and message fragmentation.  In a network with
   problems, the likelihood of getting one single-packet message
   delivered successfully is higher than getting two message fragments
   delivered successfully.  Therefore, using a larger size may prevent
   the operator from getting some critical information about the
   problem, whereas using small messages might get that information to
   the operator.  It is recommended that messages intended for
   troubleshooting purposes should not be larger than 480 octets.  To
   further strengthen this point, it has also been observed that some
   UDP implementations generally do not support message sizes of more
   than 480 octets.  This behavior is very rare and may no longer be an
   issue.

   There are other use cases where syslog messages are used to transmit
   inherently lengthy information, e.g., audit data.  By not enforcing
   any upper limit on the message size, syslog applications can be
   implemented with any size needed and still be compliant with this
   document.  In such cases, it is the operator's responsibility to
   ensure that all components in a syslog infrastructure support the
   required message sizes.  Transport mappings may recommend specific
   message size limits that must be implemented to be compliant.

   Implementers are reminded that the message length is specified in
   octets.  There is a potentially large difference between the length
   in characters and the length in octets for UTF-8 strings.

   It must be noted that the IPv6 MTU is about 2.5 times 480.  An
   implementation targeted towards an IPv6-only environment might thus
   assume this as a larger minimum size.

參考

RFC 5424: The Syslog Protocl §6, Appendix A
延伸閱讀
  • RFC 3164 有描述 relay 行為,RFC5424 沒有 (可能會放在不同文件)

2018年12月22日 星期六

SDP simcap

RFC 3407:SDP Simple Capability Declaration (simcap)
SDP 簡易通話能力宣告

定義一組新 SDP 屬性,在一個 session 描述裡放一個 capability set,列出所有支援的媒體功能,可作為其它 session 協商的資訊來源。

SDP simcap 新增的屬性有:
a=sqn: <sqn-num>
a=cdsc: <cap-num> <media> <proto> <fmt list>
a=cpar: <cap-par>
a=cparmin: <cap-par>
a=cparmax: <cap-par>
可以放在 session-level 或 media-level。一個 capability set 以 a=sqn 開始,緊接著第 1 個 a=cdsc,剩下可以散落各處。

a=sqn 說明 capability set 的序號 (sequence number),<sqn-num> 從 0 開始。每次新發表一個 capability set 取代舊的,序號加 1 後 modulo 256。接收端可能不會收到所有序號的 capability set,不能因為有跳號而拒絕。

a=cdsc 是 capability description,類似 m= 行,不需要 <port>,但多了 <cap-num>。全部 a=cdsc 中的 fmt 從 1 開始編號,<cap-num> 是每個 a=cdsc 中第一個 fmt 的編號。如此一來,傳送或接收端、<sqn-num>、加上 fmt 的編號就可以參照到一個特定媒體。接收端不能因為 fmt 編號有跳號而拒絕。
註:fmt 本身在不同 a=cdsc 可能重複。
註:包含在 m= 行的所有 fmt 也必須出現在 capability set。

cpar、cparmin、cparmax 可分別指定 capability 能支援的參數值、最小值、和最大值。
<cap-par> 可以是 "b=" 頻寬資訊或完整的 "a=" 屬性。

範例:
v=0
o=- 25678 753849 IN IP4 128.96.41.1
s=
c=IN IP4 128.96.41.1
t=0 0
m=audio 3456 RTP/AVP 18 96
a=rtpmap:96 telephone-event/8000
a=fmtp:96 0-15,32-35
a=sqn: 0
a=cdsc: 1 audio RTP/AVP 0 18 96
a=cpar: a=fmtp:96 0-16,32-35
a=cdsc: 4 image udptl t38
a=cdsc: 5 image tcp t38
準備送收 G.729 語音和 telephone-event 0-15、32-35。告知對方另外還可以支援:
  • PCMU 語音
  • telephone event 16
  • udp 或 tcp T.38 傳真 (見 T.38 Annex D, "SIP/SDP Call Establishment Procedures").
註:a=rtpmap:96 已 specified,所以沒包含在 capability description

範例 (多個媒體串流):
v=0
o=- 25678 753849 IN IP4 128.96.41.1
s=
c=IN IP4 128.96.41.1
t=0 0
m=audio 3456 RTP/AVP 18
a=sqn: 0
a=cdsc: 1 audio RTP/AVP 0 18
m=video 3458 RTP/AVP 31
a=cdsc: 3 video RTP/AVP 31 34
準備送收 G.729 語音和 H.261 影像。告知對方還可以支援:
  • PCMU 語音
  • H.263 影像
等效範例 (capability set 改放在 session-level):
v=0
o=- 25678 753849 IN IP4 128.96.41.1
s=
c=IN IP4 128.96.41.1
t=0 0
a=sqn: 0
a=cdsc: 1 audio RTP/AVP 0 18
a=cdsc: 3 video RTP/AVP 31 34
m=audio 3456 RTP/AVP 18
m=video 3458 RTP/AVP 31

2018年12月14日 星期五

C Library rand()

產生虛擬亂數 (pseudo-random number),不是真的亂數,也不是機密的,而是一系列看起來像亂數的數列,這些數字實際上是有固定順序的。

#include <stdlib.h>

void srand(unsigned int seed); // 設定函式庫內部的 seed,一開始預設是 1。

int rand(void); // 依據 seed 運算產生範圍為 [0, RAND_MAX] 的虛擬亂數回傳,並儲存為新的 seed。

int rand_r(unsigned int *seedp); //

rand_r() 為 reentrant 版的 rand(),需要傳入儲存 seed 的記憶體指標,在 thread 程式得以不被其它 thread 干擾來產生同樣的數列。在 POSIX.1-2008 標注為廢棄。

POSIX.1-2001 rand() 和 srand() 的實作範例:

static unsigned long next = 1;

/* RAND_MAX assumed to be 32767 */
int myrand(void) {
    next = next * 1103515245 + 12345;
    return((unsigned)(next/65536) % 32768);
}

void mysrand(unsigned int seed) {
    next = seed;
}

注意

  • 虛擬亂數是有一定順序的,知道 seed 就知道下一個產生的值。假設初始 seed 設為目前的時間秒數,已知是今年的話,大約有 225 個可能的值,縮小範圍後依現今運算能力很容易試出來。
  • 產生序列的最低 bit 固定 01010101..,其實不夠亂。
  • 1995 發現 Netscape 瀏覽器產生的 SSL session keys 使用時間和 process ID 作為 seed,猜得到,所以 SSL sessions 可在幾分鐘內破壞。...

以上經驗告訴我們,作為 secure 的虛擬亂數產生器,有兩個基本原則:

  1. seed 必須是無法預測的 (實際上只能難以預測?)。要有足夠的可能性,例如 2128 就足夠 (隨著計算能力越來越強,暴力破解越容易,需要更多 bit),所有可能出現的機率相同,沒有東西讓攻擊者可以縮小範圍。
  2. 產生虛擬亂數的 algorithm 必須是 secure,pseudorandom bits 沒有可辨別的 patterns。即使攻擊者學到或猜到一些 pseudorandom bits,也無法預測其它 pseudorandom bits。

參考

  1. https://inst.eecs.berkeley.edu//~cs161/fa08/Notes/random.pdf

2018年12月8日 星期六

SIP Messages

SIP 訊息 [RFC3261 §7]

SIP 是文本協定,使用 UTF-8 字元集 和 CRLF換行,用一般的文字的使用者界面就可以看, 通用的訊息格式用  ABNF 描述是按呢:

generic-message  =  start-line       ;開始的一行標明請求方法或回應碼。
                    *message-header  ;批頭。每個結束是換行。每個可能多行。
                    CRLF             ;空白行,表示 message-header 結束。
                    [ message-body ] ;選擇性的訊息體。

訊息內容用換行區隔,第 1 行就是 start-line,可以看出是請求還是回應。回應一開始是「SIP/2.0」,擱來是 3 個數字的回應碼。請求一開始是 Method。

擱來是足多 message-header,簡稱 header、批頭。每一個生著「名:值」,嗎是用換行結束。猶毋過一個 header 也可以分成多行,多的行開頭有空白。每個批頭 可以用 CRLF 緊接著 White Space 分成多行。

header 的結束是用一個空白行表示。最尾有時陣有 message-body,譬如放 SDP。message-body 有可能是二進位資料。

內容大部分無分大小寫,比較時大寫和小寫是同款,除了 (case-sensitive or case-insensitive?)

  • Method 和 CSeq 內的 Method。
  • SIP 和 SIPS URI 的 userinfo 部份當作是分大小寫的字串,簡化比較,包含 password 或 telephone-subscriber 格式。所以,原本在 telephone-subscriber 轉換成 userinfo,不分大小寫的部份須轉為小寫,參數順序除了 isdn-subaddress 和 post-dial 先放按照順序,其它按照名稱 lexically 排。
  • SIP-Version 字串不分大小寫,但實做必須送大寫(為什麼?)。
  • Call-ID 值。
  • value 用 quoted string,除非特別指明。
  • Date 放 RFC 1123 date。
  • 其它特別定義的地方。

在 RFC 2543,換行可以是 CR、LF、或 CRLF,RFC 3261 只能是 CRLF。

start-line

開始行就是一行,欄位用單一空白 (SP) 區隔,訊息只有請求和回應兩種,辨別符合 Request-Line 是請求、符合 Status-Line 是回應。

start-line   =  Request-Line / Status-Line
Request-Line =  Method SP Request-URI SP SIP-Version CRLF
Status-Line  =  SIP-Version SP Status-Code SP Reason-Phrase CRLF

請求和回應都有協定版本 SIP-Version,開頭必須是「SIP/」,不分大小寫,但實作必須用大寫送。接著兩個數字以「.」隔開,在 RFC 3261 是「2.0」。Status-Line 開頭是 SIP-Version,和 Request-Line 的 Method 不會相同而區別。

SIP-Version    =  "SIP" "/" 1*DIGIT "." 1*DIGIT
Method 可以是 REGISTER、INVITE、ACK、CANCELBYE 等,不會有「/」,可以跟 SIP-Version 區別。
Method = INVITEm / ACKm / OPTIONSm / BYEm / CANCELm / REGISTERm / extension-method
extension-method = token
token = 1*(alphanum / "-" / "." / "!" / "%" / "*" / "_" / "+" / "`" / "'" / "~" )

請求對象的 URI (用戶或服務),描述在 RFC 3261 Section 19.1 的 sip: 或 sips: 或 RFC 2396 的通用 URI,不能有 unescaped spaces 或控制字元,也不能用 "<>" 包起來。SIP 元件可以支援如 RFC 2806 的 "tel" URI 方案,可以用任何機制轉換成 SIP URI、SIPS URI、或其它 scheme。

Request-URI    =  SIP-URI / SIPS-URI / absoluteURI

Status-Code (回應碼) 是 3 碼數字表示請求的回應狀態,方便機器判讀。

Status-Code    =  3DIGIT

Reason-Phrase 是回應碼的簡易描述,讓人方便理解。每個回應碼有預設內容,但可以修改,例如依據請求 Accept-Language 的語言。不能使用的可顯示 ASCII 碼有 "、#、%、<、>、[、\、]、^、{、|、},其中「%」作為 escaped 使用,可用來顯示這些排除的碼。其它為什麼要排除呢?

Reason-Phrase   =  *(reserved / unreserved / escaped
                   / UTF8-NONASCII / UTF8-CONT / SP / HTAB)

message-header

每個訊息至少會有多個基本的 header。每個 header 可能跨多行。因為有少數例外,實際批頭格式是依每種批頭名列舉,通用格式 依循 RFC 2822 Section 2.2:

header = header-name HCOLON header-value *(COMMA header-value) CRLF
header-name  = token                              ;至少一個字元
HCOLON       =  *( WSP ) ":" SWS                  ;冒號,和名稱在同一行,前可有 WSP,後可有 LWS。
header-value = *(TEXT-UTF8char / UTF8-CONT / LWS) ;使用 UTF8 編碼,可有 WSP,WSP 前可換行。
COMMA        =  SWS "," SWS                       ;逗號,前後可有 LWS,讓每個值可獨立一行。

名稱 (header-name) 和值 (header-value) 用冒號區隔,如有多個值則用逗號分隔。冒號必須和名稱同行,中間可以有空白(sp 或 tab)。值可以在同一行或換行,換行開頭是 WSP。值內容中的 WS 處,可變成 LWS 換行,在接收或轉送時可取代回 SP。

不同名 header 的相對順序不重要,但需要 proxy 處理的 header (譬如 Via、Route、Record-Route、Proxy-Require、Max-Forwards、和 Proxy-Authorization) 建議放頭前,較快解析到。

有的 header 可以出現多次,相對順序就重要了。欄位允許有多個逗號分隔的值,可以分成多個同款 header,意思相同。另外 WWW-Authenticate、Authorization、Proxy-Authenticate、和 Proxy-Authorization 也可以出現多次,但不能合併成一個 header。

欄位值的格式定義是跟著每個欄位名稱,不是 TEXT-UTF8 octets 的 opaque 系列, 就是 whitespace、tokens、separators、和 quoted strings 的組合。大部分值遵循通用格式,後面可以有一系列分號分隔的成對 parameter-name 和 parameter-value:

field-name: field-value *(;parameter-name=parameter-value)

雖然可以有任意數目的參數,相同參數名稱不能出現超過 1 次。

當比較 header,header 名稱是不分大小寫的。除非額外特別說明,欄位值、參數名稱、和參數值是不分大小寫的。token 總是不分大小寫的。除非額外特別說明,值用 quoted strings 表示是分大小寫的。

https://lirobo.blogspot.com/2018/11/sip-header-fields.html

message-body

請求和回應的 message-body 使用、類型和解釋取決於請求的 Method

message-body 的媒體類型必須由 Content-Type 決定,也可以說明字元集。Content-Encoding 必須省略,除非有任何編碼 (例如壓縮) 才使用。

也可以使用定義在 RFC 2046 的 "multipart" MIME 類型。如果要送包含 multipart 的 message-body,但請求的 Accept 不含 multipart,必須送一個 session 描述為非 multipart 的 message-body。

message-body 可由二進位資料組成。
當 sender 沒有提供明確字元集參數,媒體子類型 "text" 預設使用 UTF-8。

詳細用法見 RFC 5621

message-body 長度見  Content-Length

不能使用 HTTP/1.1 的 "chunked" transfer 編碼。
註:chunk 編碼改變訊息內容傳送為一系列每個有自己大小的 chunk。

transport

一個 SIP 訊息可用一個 UDP 或其它不可靠 transport 協定的 datagram 攜帶,限制請見 RFC 3261 Section 18。

在 stream 導向 transport 之上使用 SIP 訊息,必須忽略 start-line 之前的 CRLF
[參見 RFC 2616 Section 4.1],而且 header 欄位 Content-Length 是必要的,訂出在 stream 中每個 SIP 訊息的結束。

延伸閱讀

  • SIP 訊息雖然語法在字元集和規範和 RFC 2822 有所不同,但基本格式是一樣的。
  • 除了字元集和 HTTP/1.1 不同,SIP 訊息和 header 欄位語法大部分相同,但 SIP 不是 HTTP 的擴充。此外 HTTP 只用可靠的 transport 協定傳送。
  •  SIP-Version 依循 HTTP Version (HTTP 取代為 SIP,HTTP/1.1 取代為 SIP/2.0) 關於版本順序、合規要求、和版號更新。 不像 HTTP/1.1,SIP-Version 視為文字字串,但實際上應該沒有什麼不同。
  • SIP header 欄位依循 HTTP header 欄位的語法定義,以及延伸到多行的規則。但 HTTP 是用 implicit whitespace and folding,而 RFC 3261 符合 RFC 2234 使用 explicit whitespace and folding 作為文法整體的一部分。多個名稱相同的 header 欄位,其值是逗號分隔的列表,可以結合成一個 header 欄位也應用在 SIP,但文法不同而特定規則不同。

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...