2020年4月17日金曜日

【IPMSG_DELMSG & IPMSG_RELEASEFILES】IP Messenger Protocol(Draft14版 Ver4.99)【What is IPMSG】

IP Messenger Protocolを解析中。
 以下備忘録。
 本家はVisual C++で実装。

IPMSG_DELMSG & IPMSG_RELEASEFILESの仕様

IPMSG_DELMSGは、受信者がメッセージを破棄 Receiver discards message した時に使う。
Receiver -> Sender


受信側はIPMSG_SENDMSGの応答としてIPMSG_RECVMSGを送信しているはずなので、IPMSG_DELMSGはあまり意味が無い気がする。
送信側はメッセージ全文をSENDMSGに乗せていて、
受信側はメッセージ全文を受け取っている。
読んでいませんとDELMSGを送られても、送信側は信用することができない。※たとえ、IPMSG_SECRETOPT封書指定で未開封であっても。

※Open Protocolであるため本家以外のIPMクライアントを想定しなければならない。もっと言うなら、悪意を持ったIPMクライアントがいる可能性もある。

※本家Ver4.99で非対応コマンド。送信・受信しない。ソース上に定数定義のみ存在する。

逆のケースを考えると、「誤送信したメッセージを取り消したい」というニーズは常にある。
だが、DELMSGをSender -> Receiverへ送ったとしても、削除を認めるかどうかは相手に委ねられるため、所詮"おねがい"である。
もし、「取り消し」機能を実装するならば、
  • 送信側で取り消し有効期間として1分程度の遅延送信を行う
が考えられる。




IPMSG_RELEASEFILESは、受信者が添付ファイルのダウンロード完了時など Receiver completes downloaded, etc 、添付情報が不要になった時 No attachment required に使う。
Receiver -> Sender


メッセージとデータの一貫性を保つためにTCP転送用のファイルをメモリ上にキープするようなIPM実装があるかもしれない。
その場合、システムリソースを適切に解放することが可能になる。


本家Ver4.99には実装されていないが、このコマンドはSender -> Receiver で使用したい場合もある。
送信側で添付したファイルが、「HDDから削除」や、
「誤って添付した」場合などである。
送信側がリクエストを拒否すれば済む話だが、無駄なTCPセッションが発生する。また、破棄されたことを認識していないIPMクライアントが、無駄なリトライを繰り返す事を阻止できる。



2020年4月8日水曜日

【概要】IP Messenger Protocol(Draft14版 Ver4.99)【What is IPMSG】

IP Messenger Protocolを解析中。
以下備忘録。
本家はVisual C++で実装。

公式リンク Official Web Site



基本情報 Network

Socket
UDPport 2425汎用 Common
Not Binary, but mixed charset.
Packet size limit 32KB.
TCPファイル送受信 Send File
Binary or
Header text & Binary.
Header size limit 1KB.
File size unremitted.


文字コード Charset

通常 -> ASCII + Windows-31J ( = MS932) [+UTF-8]

※本家Protocol.txtに明記

IPMSG_UTF8OPT -> ASCII + UTF-8

※旧バージョン互換 Compatibility with Previous Versionsのため、BR系ではWindows-31J Only。
 OPT != {IPMSG_BR_ENTRY, IPMSG_BR_EXIT, IPMSG_BR_ABSENCE(=NOTIFY)}

※UTF-8 受信能力 Reception CapabilityはIPMSG_CAPUTF8OPTで示す。
※IPMSG_BR_EXITについてProtocol.txtでの記述が曖昧だが、コマンドの性質上IPMSG_BR_ENTRYと同一の配慮が必要だと思われる。


改行文字はOS・Charsetによらず、Always LF(0x0a) Only。Must CR+LF replace to LF。


英語圏ではASCII、日本以外では各国版Windowsで標準的に使われる文字コードで運用されている可能性あり。もし、ローカライズされたWindows Charsetの中にASCIIベースでないものがあった場合、いろいろと不都合が発生する。


パケット構造 Packet Format

:は半角コロン、0x3a
nullはnull文字(\0)、0x00。C言語系では終端文字扱いにできて便利っぽい。
\nはLF、0x0a
\aはBEL、0x07
\bはBS、0x08
=は半角イコール、0x3d
,は半角カンマ、0x2c


[ ]および{ }は省略可能 optionalであることを示す。

基本型 Base


バージョンパケット番号ユーザ名ホスト名コマンド&オプション[追加部(データ部null拡張部)]

VersionPacket NoUser NameHost NameCommand Mode & Options[Additional Section(DatanullExtend Data)]


実態 Implicit Format
共通部{null[第1拡張部][null第2拡張部]}
Common Section{null[Extend Section][nullSecond Extend Section]}

共通部 Common Section
バージョンパケット番号ユーザ名ホスト名コマンド&オプション[データ]
VersionPacket NoUser NameHost NameCommand Mode & Options[Data]


ex) 1:123456:Michael:PC2020 A44:32:Hello World
    パケット番号123456、送信者Michael、メッセージ送信「Hello World」

※UserNameとHostNameはis ASCII only。
 Version~Command section はASCII only。Data以降はWindows-31JまたはUTF-8


※実装時は、UDP受信したバイト配列を前方から0x3aをseekして、コマンド&オプションをASCIIで変換し、IPMSG_UTF8OPTの有無により、各セクションを適切なCharsetで解釈しなければならない。バイト配列をまるっとWindows-31Jに変換してでsplitでは全くダメ。

※パケット番号Packet No isは Unique一意でなければならない。C++実装Ver4.50ではUnix epochを使用し、もしそれが使用済み(1秒以内の生成)であれば前回パケット番号+1を使用している。

※User Name, Host Nameにcan't include nullを含むことはできないが、追加部 Additional Sectionにはmay contains nullを含む場合がある。

※Packet Formatの各Valueには、セクションごとのControl characters per section 制御文字(null\n\a\b)を含まないようにInput Validate入力時にEscape or Trimしなければならない。
※コマンドによっては追加部が省略される場合がある。

User Name Sectionに公開鍵指紋(2048bit RSAのSHA-1署名のhex値)を付けることができる。詳細はmore info Protocol.txt参照。
  User Name<SHA-1>
 -は半角ハイフン、0x2d <は半角カッコ、0x3C >は半角カッコ、0x3E
 実装時は正規表現 RegExpで展開する。

BR系 Command == {IPMSG_BR_ENTRY, IPMSG_BR_EXIT, IPMSG_BR_ABSENCE(=NOTIFY), IPMSG_ANSENTRY}


バージョンパケット番号ユーザ名ホスト名コマンド&オプション[ニックネーム]{null[グループ名]null[\nUNユーザ名UTF-8][\nHNホスト名UTF-8][\nNNニックネームUTF-8][\nGNグループ名UTF-8][\nVSアプリバージョン番号][\nPLポールリクエスト][\nIPIP Dict]}

VersionPacket NoUser NameHost NameCommand Mode & Options[Nick Name]{null[Group Name]null[\nUNUser Name UTF-8][\nHNHost Name UTF-8][\nNNNick Name UTF-8][\nGNGroup Name UTF-8][\nVSApp Version Code][\nPLPoll Request][\nIPIP Dict] }

ex)1:123456:Michael:PC2020 A44:535101443:Michael[出家]nullG-1null\nGN:G-1

※追加部 Additional Sectionはデータ部Data partとしてNickName、拡張部 ExData partとしてGroupName、第2拡張部(UTF-8拡張) second ExData partとしてdelimited nullを挟んでMetaInfoが入る。MetaInfo is は全てall UTF-8 partであるため、丸ごとエンコーディング First step, apply UTF-8 to MetaInfoを適用してから、展開して構わない。

※第2拡張部(UTF-8拡張) If contains second ExData part を含める場合、because it's UTF-8であるから、送信者は必ずmust be IPMSG_CAPUTF8OPTを立てなければならない。

※3MetaInfoはProtocol.txtで多言語(UTF-8)拡張と呼ばれているが、そうじゃないものを含むので、ここでは勝手にMetaInfoと呼ぶ。

※"UN"~"IP"は順不同 Un ordered。

"IP"(IP Dict)については公式資料が無い。UTF-8のIP Messenger Protocol New Format。IPBASE64("IP2Key1Value1Key2Value2・・・Z")
"PL"は詳細不明だが、int(32bit)の数字が入っている。

※アプリバージョン番号 App Version Codeのformatは、("%08X:%d:%d:%d", ULong(32bit) 定数, UShort(16bit) Major, UShort(16bit) Minor, UShort(16bit) Revision)。

※IPMSG_ANSENTRYの時、"VS"以外の"UN"~"IP"は含めない。
※※相手がUTF-8に対応していないならUTF-8文字列を送る意味がないし、相手がUTF-8対応しているなら前出の各NameがUTF-8で送信されるため。

※旧バージョン互換のためIPMSG_BR_ENTRY, IPMSG_BR_EXIT, IPMSG_BR_ABSENCE(=NOTIFY)の時、Can't IPMSG_UTF8OPT

※不在中はIPMSG_ABSENCEOPTを立ててNick Nameを含める。Nick Nameは不在情報を付加した表示名(本家はPC Account or Manual Edit + "[××中]")である。

null前のNick Name、Group Nameはそれぞれoptional 省略できる。両方省略するとnullnull\nとなる。C++実装Ver4.90ではnull\nでも以降を解釈するよう対策がされている。

※"UN"~"IP"は省略可。セクションごと"XX・・・\n"を省略すればよい。

※Group Name以降(First null以降)は初期仕様に無い拡張であり、完全に省略しても構わない。

\nは終端というよりdelimiterと考える。

※実装時は、基本型にならって分割した後、追加部 Additional Sectionをnullで3つにsplitし、1Nick Name、2Group Name、3MetaInfo(UTF-8)セクションとする。MetaInfo(UTF-8)は\nでいくつかに分割し、頭文字 Start With(UN、HN、NN、GN、VS、PL、IP)からそれぞれ情報を取り出す。

※Protocol.txtとC++実装Ver4.90とでBR系パケットの判定に相違があるものの、IPMSG_BR_ENTRY, IPMSG_BR_EXIT, IPMSG_BR_ABSENCE(=NOTIFY), IPMSG_ANSENTRYは同一の構造で問題ないと思われる。IPMSG_BR_EXITの時、追加部は意味がない気が・・・
※IPMSG_DIR_POLLについて、BR系パケットと似た構成であるが、詳細調査中。


メッセージ送信 IPMSG_SENDMSG


バージョンパケット番号ユーザ名ホスト名コマンド&オプションメッセージ{null[ファイル共有(添付)情報[\nファイル共有(添付)情報2]・・・][null宛先情報]}

VersionPacket NoUser NameHost NameCommand Mode & OptionsMessage{null[File Attach Info[\nFile Attach Info 2]・・・][nullDestination List]}

ex) 1:123456:Michael:PC2020 A44:32:Hello World


ファイル共有情報 File Attach Info

ファイルIDファイル名ファイルサイズ更新日時(1970/1/1経過秒)ファイル属性[拡張属性=Value[Value2]・・・][拡張属性2]・・・

File IDFile NameFile SizeModified time(Unix Epoch time)File Attribute[Ex File Attribute=Value[Value2]・・・][Ex File Attribute 2]・・・

ex-File Attach Info) 1:secret.jpeg:6f:26a68:20:8=b
    secret.jpeg(111Byte)、メッセージ埋込画像 IPMSG_FILE_CLIPBOARD(推奨本文挿入offset 11 char)

ex-Full) 1:123456:Michael:PC2020 A44:32:Hello Worldnull1:secret.jpeg:6f:26a68:20:8=b

※ファイルサイズと更新日時(Unix Epoch time)はlong(64bit)、ファイル属性Attr・拡張属性Ex Attr & Valueはunsigned int(32bit)。それぞれ16進数 Hexで表現する。
※※拡張属性Ex Valueについて、change unsigned int(32bit) to plain text in the futureプレーンテキストへ変更される可能性がある。※いかにも文字列を設定できそうなキー名がある。

※File ID の形式について明記 Unwrittenされていないが、C++実装Ver4.50では、送受信ともにint(32bit)の10進数 Decimalで表現している。
※※TCP時のIPMSG_GETDIRFILESの時のFile IDはhexとなる。

※ファイル名にif file name include を含む場合、replace to ::

Must be IPMSG_FILEATTACHOPTが必須。事前に、送信先のエントリー系パケットで添付ファイル送受信能力を示すIn advance, Sender and Receiver have a flag of IPMSG_FILEATTACHOPT when entryが立っていなければならない。


宛先情報 Destination List ※Like "CC" Mail

TO[ユーザ名ホスト名[\aユーザ名2ホスト名]・・・]null

TO[User NameHost Name[\aUser Name2HostName2]・・・]null

ex-Destination List) TO:Michael:PC2020 A44\aEva:PC2020 A45null

ex-Full) 1:123456:Michael:PC2020 A44:32:Hello Worldnull1:secret.jpeg:6f:26a68:20:8=b
nullTO:Michael:PC2020 A44\aEva:PC2020 A45null

※最大30件。Limit count of list items = 30.

※末尾のnullは必要?? Protocol.txtではそのように指示。

Must be IPMSG_ENCEXTMSGOPTが必須。つまりは暗号化も必須。In other words, encryption must also be done.


※暗号時 IPMSG_ENCRYPTOPTはMessage Sectionが暗号形式 Encrypted Format Dataとなる。

ファイル添付・宛先情報を暗号文に含める IPMSG_ENCEXTMSGOPTの場合はMessage{null[File Attach Info[\nFile Attach Info2]・・・][nullDestination List]}までが暗号形式 Encrypted Format Dataとなる。

※実装時は、first nullおよびsecond nullでsplitし、それぞれ基本データ(Version~Data Section)、File Attach Info List、Destination Listとする。File Attach Info Listを連続しないNon-contiguous でsplitする。取り出した配列array partsを6個ごとにFile Attach Infoとして構成する。数値numeriは全てhex to decimal変換する。


ファイル送受信 Send File

TCP Connect
IPMSG_GETFILEDATAパケット ==>
<== [binary data]
Close



TCP Connect
IPMSG_GETDIRFILESパケット ==>
<== file1 in root directory header[binary data]
<== file2 in root directory header[binary data]
<== ・ 
<== ・ 
<== ・ 
<== directory1 in root directory header
<== file3 in directory1 header[binary data]
<==  ・ 
<==  ・ 
<==  ・ 
<== Return-Parent header
<== ・ 
<== ・ 
<== ・ 
<== Return-Parent header
Close


※0byteのファイルを許容し、binary dataは省略される。0byteの時、IPMSG_GETFILEDATAリクエストはそもそも不要で、IPMSG_GETDIRFILESの時はファイルヘッダだけが来てbinary dataは省略され次のファイルヘッダと続く。