Slide 1

Slide 1 text

第一回データベースについて語 る会 株式会社 Nextbeat 富永 孝彦 2024年7月26日 (金)

Slide 2

Slide 2 text

はじめに MySQLのクライアント作成の時の話ですが、他のデータベースでも同じようなことが言えるかもしれません。 自分が初めて作成した時の知見を元に発表を行いますので、資料に間違った情報があるかもしれませんがご了 承ください。 間違っている箇所や、もっとこうした方が良いなどありましたら教えてください! 資料 今回はデータベースのクライアント作成に関する話

Slide 3

Slide 3 text

自己紹介 名前: 富永 孝彦 (とみなが たかひこ) 所属: 株式会社 Nextbeat SNS X: @takapi327 Github: https://github.com/takapi327 趣味 プログラミング大好き

Slide 4

Slide 4 text

なぜこの話を? データベースクライアント作成に関する情報が少ない 自分が作る時情報が欲しかった 手順 便利ツール ドキュメント etc… そういった方の参考になればと思いこの話をします。

Slide 5

Slide 5 text

背景と目的 Scalaは現在JVM, JS, Nativeというマルチプラットフォームに対応しています。 しかし、JDBCを使用したライブラリだとJVM環境でしか動作しません。 そのためMySQLプロトコルに対応したプレーンなScalaで書かれたコネクタを提供(こちらはまだ誠意開発中)す ることで、異なるプラットフォームで動作できるようにするために開発を行っています。 クライアント自体はScalaを使用して作成しました。

Slide 6

Slide 6 text

要は… 趣味です

Slide 7

Slide 7 text

開発のTips 通信の監視 パケットの見方 カンニング ここではどんな感じで開発を進めていけばいいのかわからないという方のために、参考になれば幸いです。

Slide 8

Slide 8 text

通信の監視 なぜ通信の監視を行うと良いのか?

Slide 9

Slide 9 text

通信の監視 なぜ通信の監視を行うと良いのか? ⚫︎ パケットの見方を覚える

Slide 10

Slide 10 text

通信の監視 なぜ通信の監視を行うと良いのか? ⚫︎ パケットの見方を覚える ⚫︎ 答え合わせを行いながら開発できる

Slide 11

Slide 11 text

通信の監視 なぜ通信の監視を行うと良いのか? ⚫︎ パケットの見方を覚える ⚫︎ 答え合わせを行いながら開発できる ではそのパケットを見る方法をみていきましょう

Slide 12

Slide 12 text

データベースクライアントとMySQLサーバーとの通信 データベースクライアントとMySQLサーバーとの通信は、TCP/IPプロトコルを使用して行われます。 こういった通信はネットワークパケットアナライザと呼ばれるngrepなどのツールを使用して監視することで、 通信内容を確認できます。 ※ 他にも色々なツールがあるので、自分に合ったものを選ぶと良いです。 1 brew install ngrep

Slide 13

Slide 13 text

ngrepの使用方法 1 sudo ngrep -x -q -d lo0 '' 'port 3306'

Slide 14

Slide 14 text

ngrepの使用方法 `-x`: キャプチャしたパケットのデータ部分を16進数形式で表示します。各バイトが2桁の16進数で表示され、可読なASCII文字が表示されます。 1 sudo ngrep -x -q -d lo0 '' 'port 3306'

Slide 15

Slide 15 text

ngrepの使用方法 `-x`: キャプチャしたパケットのデータ部分を16進数形式で表示します。各バイトが2桁の16進数で表示され、可読なASCII文字が表示されます。 `-q`: 出力の際にパケットのデータ部分のみを表示し、その他の情報を抑制します。これは「クワイエットモード」 (quiet mode)として知られています。 1 sudo ngrep -x -q -d lo0 '' 'port 3306'

Slide 16

Slide 16 text

ngrepの使用方法 `-x`: キャプチャしたパケットのデータ部分を16進数形式で表示します。各バイトが2桁の16進数で表示され、可読なASCII文字が表示されます。 `-q`: 出力の際にパケットのデータ部分のみを表示し、その他の情報を抑制します。これは「クワイエットモード」 (quiet mode)として知られています。 `-d`: デバイスを指定します。ここでは`lo0`を指定しています。`lo0`はループバックインターフェースを指しています。ループバックインターフェースは、ローカル ホスト(`127.0.0.1`)との通信を行うために使用されます。 1 sudo ngrep -x -q -d lo0 '' 'port 3306'

Slide 17

Slide 17 text

ngrepの使用方法 `-x`: キャプチャしたパケットのデータ部分を16進数形式で表示します。各バイトが2桁の16進数で表示され、可読なASCII文字が表示されます。 `-q`: 出力の際にパケットのデータ部分のみを表示し、その他の情報を抑制します。これは「クワイエットモード」 (quiet mode)として知られています。 `-d`: デバイスを指定します。ここでは`lo0`を指定しています。`lo0`はループバックインターフェースを指しています。ループバックインターフェースは、ローカル ホスト(`127.0.0.1`)との通信を行うために使用されます。 `''`: フィルタ条件として空の文字列を指定しています。これは、特定の内容に関するフィルタリングを行わないことを意味します。すべてのパケットをキャプチャ 対象とする場合に使用されます。 1 sudo ngrep -x -q -d lo0 '' 'port 3306'

Slide 18

Slide 18 text

ngrepの使用方法 `-x`: キャプチャしたパケットのデータ部分を16進数形式で表示します。各バイトが2桁の16進数で表示され、可読なASCII文字が表示されます。 `-q`: 出力の際にパケットのデータ部分のみを表示し、その他の情報を抑制します。これは「クワイエットモード」 (quiet mode)として知られています。 `-d`: デバイスを指定します。ここでは`lo0`を指定しています。`lo0`はループバックインターフェースを指しています。ループバックインターフェースは、ローカル ホスト(`127.0.0.1`)との通信を行うために使用されます。 `''`: フィルタ条件として空の文字列を指定しています。これは、特定の内容に関するフィルタリングを行わないことを意味します。すべてのパケットをキャプチャ 対象とする場合に使用されます。 `'port 3306'`: キャプチャ対象のパケットをTCPまたはUDPのポート番号`3306`に限定します。この場合、MySQLデータベースが通常使用するポート番号3306に対 するパケットがキャプチャ対象となります。 1 sudo ngrep -x -q -d lo0 '' 'port 3306'

Slide 19

Slide 19 text

ngrepの使用方法 `-x`: キャプチャしたパケットのデータ部分を16進数形式で表示します。各バイトが2桁の16進数で表示され、可読なASCII文字が表示されます。 `-q`: 出力の際にパケットのデータ部分のみを表示し、その他の情報を抑制します。これは「クワイエットモード」 (quiet mode)として知られています。 `-d`: デバイスを指定します。ここでは`lo0`を指定しています。`lo0`はループバックインターフェースを指しています。ループバックインターフェースは、ローカル ホスト(`127.0.0.1`)との通信を行うために使用されます。 `''`: フィルタ条件として空の文字列を指定しています。これは、特定の内容に関するフィルタリングを行わないことを意味します。すべてのパケットをキャプチャ 対象とする場合に使用されます。 `'port 3306'`: キャプチャ対象のパケットをTCPまたはUDPのポート番号`3306`に限定します。この場合、MySQLデータベースが通常使用するポート番号3306に対 するパケットがキャプチャ対象となります。 このコマンドは、MySQLサーバーへのすべてのトラフィック(ループバックインターフェース経由で送受信さ れるもの)を16進数形式で出力します。 1 sudo ngrep -x -q -d lo0 '' 'port 3306'

Slide 20

Slide 20 text

ngrepの使用方法 // step 1 sudo ngrep -x -q -d lo0 '' 'port 3306' 1 2

Slide 21

Slide 21 text

ngrepの使用方法 sudo ngrep -x -q -d lo0 '' 'port 3306' 1 // step 1 2

Slide 22

Slide 22 text

ngrepの使用方法 // step 1 sudo ngrep -x -q -d lo0 '' 'port 3306' 1 2

Slide 23

Slide 23 text

ngrepの使用方法 // step 2 sudo ngrep -x -q -d lo0 '' 'port 3306' Password: 1 2 3

Slide 24

Slide 24 text

ngrepの使用方法 Password: 1 // step 2 2 sudo ngrep -x -q -d lo0 '' 'port 3306' 3

Slide 25

Slide 25 text

ngrepの使用方法 // step 2 sudo ngrep -x -q -d lo0 '' 'port 3306' Password: 1 2 3

Slide 26

Slide 26 text

ngrepの使用方法 // step 3 sudo ngrep -x -q -d lo0 '' 'port 3306' Password: interface: lo0 (127.0.0.0/255.0.0.0) filter: ( port 3306 ) and (ip || ip6) 1 2 3 4 5

Slide 27

Slide 27 text

ngrepの使用方法 interface: lo0 (127.0.0.0/255.0.0.0) filter: ( port 3306 ) and (ip || ip6) 1 // step 3 2 sudo ngrep -x -q -d lo0 '' 'port 3306' 3 Password: 4 5

Slide 28

Slide 28 text

ngrepの使用方法 // step 3 sudo ngrep -x -q -d lo0 '' 'port 3306' Password: interface: lo0 (127.0.0.0/255.0.0.0) filter: ( port 3306 ) and (ip || ip6) 1 2 3 4 5

Slide 29

Slide 29 text

ngrepの使用方法 // step 4 (別タブで実行) mysql -u root -p 1 2

Slide 30

Slide 30 text

ngrepの使用方法 // step 5 sudo ngrep -x -q -d lo0 '' 'port 3306' Password: interface: lo0 (127.0.0.0/255.0.0.0) filter: ( port 3306 ) and (ip || ip6) T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password. 1 2 3 4 5 6 7 8 9 10 11 12

Slide 31

Slide 31 text

ngrepの使用方法 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password. 1 // step 5 2 sudo ngrep -x -q -d lo0 '' 'port 3306' 3 Password: 4 interface: lo0 (127.0.0.0/255.0.0.0) 5 filter: ( port 3306 ) and (ip || ip6) 6 7 8 9 10 11 12

Slide 32

Slide 32 text

ngrepの使用方法 // step 5 sudo ngrep -x -q -d lo0 '' 'port 3306' Password: interface: lo0 (127.0.0.0/255.0.0.0) filter: ( port 3306 ) and (ip || ip6) T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password. 1 2 3 4 5 6 7 8 9 10 11 12

Slide 33

Slide 33 text

パケットの見方 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 34

Slide 34 text

パケットの見方 この部分はパケットがどこからどこへ送信されたかを示しています。ここでは、MySQLサーバーからMySQLク ライアントへの通信を示しています。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 35

Slide 35 text

パケットの見方 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 36

Slide 36 text

パケットの見方 この部分はパケットのデータ部分を示しています。ここでは、MySQLサーバーからMySQLクライアントへの通 信データを示しています。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 37

Slide 37 text

パケットの見方 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 38

Slide 38 text

パケットの見方 パケットは各2桁の16進数が1バイトを表しています。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 39

Slide 39 text

パケットの見方 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 40

Slide 40 text

パケットの見方 MySQLでは最初の4バイトはヘッダー部分を示しており、それ以外の部分はボディのデータ部分を示していま す。 ペイロードの長さ (最初の3バイト) Sequence ID (最後の1バイト) 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 41

Slide 41 text

パケットの見方 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 42

Slide 42 text

パケットの見方 この右に表示されているデータは、パケットのデータ部分をASCII文字列で表現したものです。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 43

Slide 43 text

パケットの形式 送信されるパケットの内容はデータベースによって決まっている。 データベースごとにドキュメントが存在しているので、それを参照することでパケットの内容を理解すること ができる。 ※ MySQL v8から通信方式がSSL前提になっているので、パケットを表示しても暗号化されて解読できなくなっ ています。 なので、v8を使用する場合は、認証プラグインを native_password に変更するか、SSLを無効に することで解読できるようになります。

Slide 44

Slide 44 text

MySQL MySQLは、オープンソースのリレーショナルデータベース管理システム(RDBMS)です。 MySQL用のデータベースクライアントライブラリを作成する際には、MySQLの通信プロトコルについて理解す ることが重要です。 以下がMySQLの公式ドキュメントです。 https://dev.mysql.com/doc/dev/mysql-server/latest/

Slide 45

Slide 45 text

これが(個人的に)すごくわかりづらかった… 型の説明がない 別のページにある 説明がよくわからない 表に条件分岐をいれないで欲しい サンプルコードがない 初めて作る人用というよりは、知識を持っている人が 確認するための資料なのかも?

Slide 46

Slide 46 text

MySQLサーバーとの通信はコネクションフェーズとコマンドフェーズに分かれて行われます。 コネクションフェーズ ハンドシェイク SSL認証 認証 コマンドフェーズ クエリの実行 プリペアドステートメントの実行 ストアドプロシージャの実行

Slide 47

Slide 47 text

MySQLサーバーとの通信はコネクションフェーズとコマンドフェーズに分かれて行われます。 コネクションフェーズ ハンドシェイク SSL認証 認証 まず初めはハンドシェイクから見ていくことになる。 https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html

Slide 48

Slide 48 text

ヘッダー これがハンドシェイク時にMySQLサーバーからクライ アントに送信されるパケットの内容です。 ボディ

Slide 49

Slide 49 text

読み方 int string string[number] $length ※ 他にもあるけど今日はこの4つ

Slide 50

Slide 50 text

int : 固定長整数 (Protocol::FixedLengthInteger) 固定長整数は、値を固定されたバイト数で表現するためのデータ型です。 整数の値は、バイト列の中に格納されます。 MySQLプロトコルでは、以下のような固定長の符号なし整数のバリアントがあります。 int<1> : 1バイトの固定長整数 int<2> : 2バイトの固定長整数 int<3> : 3バイトの固定長整数 int<4> : 4バイトの固定長整数 int<6> : 6バイトの固定長整数 int<8> : 8バイトの固定長整数

Slide 51

Slide 51 text

固定長整数の例 例えば、3バイトの固定長整数(int<3>)の場合。3バイトのデータで一つの整数を表現します。 バイト順序 16進数 10進数 バイト1 0x01 1 バイト2 0x02 2 バイト3 0x03 3 この場合、値は0x010203となり、これを10進数に変換すると291になります。 つまり、int<3>というデータ型の場合はバイト配列の内、3つのバイトで表現を行っているということです。

Slide 52

Slide 52 text

パケットに当てはめてみる ヘッダーのドキュメントを見ると、ヘッダーは以下のようになっています。 ペイロードの長さ: int<3> シーケンスID: int<1> ※ string はペイロードの値全てなので、後で分解して説明

Slide 53

Slide 53 text

ペイロードの長さ: int<3> つまり 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 54

Slide 54 text

ペイロードの長さ: int<3> つまり この3桁がペイロードの長さを示しています。この場合、0x4a000000を10進数に変換すると74になります。 ※ つまり、このパケットのペイロード部分は74個のバイトで構成されているということ 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 55

Slide 55 text

シークエンスID: int<1> つまり 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 56

Slide 56 text

シークエンスID: int<1> つまり この1桁がシークエンスIDを示しています。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 57

Slide 57 text

string : NULL終端文字列 (Protocol::NullTerminatedString) MySQLプロトコルでは、文字列のデータをエンコードする際にいくつかの方法があります。その中でも、 string は、文字列の最後が NULL (00)で終端された文字列のことを指します。

Slide 58

Slide 58 text

例えば、文字列 "Hello" を string としてエンコードすると以下のようになります: 文字列 16進数 H 0x48 e 0x65 l 0x6c l 0x6c o 0x6f NULL 0x00 この場合、文字列 "Hello" は16進数で "48 65 6C 6C 6F 00" と表現されます。最後の00バイトが、文字列の終わ りを示すNULL終端文字です。

Slide 59

Slide 59 text

string[number] : 固定長文字列 (Protocol::FixedLengthString) string[number] とは、固定長の文字列で、事前に決められた長さを持つ文字列のことです。文字列の長さは 予め決まっており、これが固定されています。 例えば、 string[5] は、5バイトの固定長文字列を表します。この場合、文字列の長さは5バイトで、文字列 の長さが5バイトになるようにパディングされます。 文字列データ 16進数表示 説明 "12345" 31 32 33 34 35 ちょうど5バイトの文字列 "AB" 41 42 00 00 00 2文字の文字列を5バイトに拡張

Slide 60

Slide 60 text

$length : 可変長文字列 (Protocol::LengthEncodedString) $length とは、文字列の先頭にその文字列の長さを表すエンコードされた整数が付加される形式の文字列で す。これにより、文字列の長さを事前に知ることができます。 長さエンコードされた整数は、特別な形式でエンコードされます。一般的に、1バイトから最大8バイトで表現 されます。

Slide 61

Slide 61 text

例えば、長さが5の文字列 "Hello" の場合: データ バイト例 長さ (5) 05 文字列 "Hello" 48 65 6C 6C 6F つまり、"Hello" の長さエンコードされた文字列は "05 48 65 6C 6C 6F" となります。

Slide 62

Slide 62 text

パケットに当てはめてみる ハンドシェイクのドキュメントを見ると以下のように なっています。 プロトコルバージョン: int<1> サーバーバージョン: string スレッドID: int<4> 認証プラグインのランダムデータ1: string<8> 認証プラグインのランダムデータ2: $length etc…

Slide 63

Slide 63 text

プロトコルバージョン: int<1> つまり 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 64

Slide 64 text

プロトコルバージョン: int<1> つまり この1桁がプロトコルバージョン(`int<1>`)を示しています。常に10進数の10になります。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 65

Slide 65 text

サーバーバージョン: string つまり 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 66

Slide 66 text

サーバーバージョン: string つまり ここで0がきているので、NULL終端文字列の終わりを示しているとわかる。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 67

Slide 67 text

サーバーバージョン: string つまり 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 68

Slide 68 text

サーバーバージョン: string つまり なので、ここまでのデータはサーバーバージョンを示しているとわかる。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 69

Slide 69 text

スレッドID: int<4> つまり 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 70

Slide 70 text

スレッドID: int<4> つまり この4桁がスレッドID(`int<4>`)を示しています。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 71

Slide 71 text

認証プラグインのランダムデータ1: string<8> つまり 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 72

Slide 72 text

認証プラグインのランダムデータ1: string<8> つまり この8桁が認証プラグインのランダムデータ1を示しています。 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 73

Slide 73 text

認証プラグインのランダムデータ2の長さ: $length $length は、可変長文字列を示すデータ型で最初に長さを示す整数が付加される形式の文字列でした。 ここでドキュメントを見てみると…

Slide 74

Slide 74 text

認証プラグインのランダムデータ2の長さ: $length $length は、可変長文字列を示すデータ型で最初に長さを示す整数が付加される形式の文字列でした。 ここでドキュメントを見てみると…

Slide 75

Slide 75 text

認証プラグインのランダムデータ2の長さ: $length $length は、可変長文字列を示すデータ型で最初に長さを示す整数が付加される形式の文字列でした。 ここでドキュメントを見てみると… ランダムデータ2の長さは、どの条件でも`int<1>`で表現されると書いてある。

Slide 76

Slide 76 text

認証プラグインのランダムデータ2の長さ: int<1> つまり (※ 先ほどからの間にあるパケットは省略しています) 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 77

Slide 77 text

認証プラグインのランダムデータ2の長さ: int<1> つまり (※ 先ほどからの間にあるパケットは省略しています) この1桁が長さを示しています。15は10進数で17になります。 今回取得したいデータは、`$len=MAX(13, length of auth-plugin-data - 8)`という条件があるので、取得した長 さを使い計算すると 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 78

Slide 78 text

認証プラグインのランダムデータ2の長さ: int<1> つまり (※ 先ほどからの間にあるパケットは省略しています) この1桁が長さを示しています。15は10進数で17になります。 今回取得したいデータは、`$len=MAX(13, length of auth-plugin-data - 8)`という条件があるので、取得した長 さを使い計算すると `$len=MAX(13, 17 - 8)` 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 79

Slide 79 text

認証プラグインのランダムデータ2の長さ: int<1> つまり (※ 先ほどからの間にあるパケットは省略しています) この1桁が長さを示しています。15は10進数で17になります。 今回取得したいデータは、`$len=MAX(13, length of auth-plugin-data - 8)`という条件があるので、取得した長 さを使い計算すると `$len=MAX(13, 17 - 8)` `$len=MAX(13, 9)` 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 80

Slide 80 text

認証プラグインのランダムデータ2の長さ: int<1> つまり (※ 先ほどからの間にあるパケットは省略しています) この1桁が長さを示しています。15は10進数で17になります。 今回取得したいデータは、`$len=MAX(13, length of auth-plugin-data - 8)`という条件があるので、取得した長 さを使い計算すると `$len=MAX(13, 17 - 8)` `$len=MAX(13, 9)` `$len=13` 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 81

Slide 81 text

認証プラグインのランダムデータ: $length つまり 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 82

Slide 82 text

認証プラグインのランダムデータ: $length つまり この範囲(13)のデータが認証プラグインのランダムデータを示しています。 ※ 間にreserved: `string[10]`があるので、その値は飛ばしています。(全て0の値) 1 T 127.0.0.1:3306 -> 127.0.0.1:56281 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 9b 19 00 00 J....8.0.33..... 3 69 26 73 2a 75 08 1c 2c 00 ff ff ff 02 00 ff df i&s*u..,.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 63 01 02 01 2e ...........c.... 5 3e 45 23 57 31 7f 5b 00 63 61 63 68 69 6e 67 5f >E#W1.[.caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 83

Slide 83 text

これで数字と文字列は読み取れるようになった! 読み方がわかればそんなに難しくないと思いませんか?

Slide 84

Slide 84 text

これで数字と文字列は読み取れるようになった! 読み方がわかればそんなに難しくないと思いませんか? 数字と文字列の読み方がわかれば他のデータも大体読めるようになります。

Slide 85

Slide 85 text

これで数字と文字列は読み取れるようになった! 読み方がわかればそんなに難しくないと思いませんか? 数字と文字列の読み方がわかれば他のデータも大体読めるようになります。 でも...読めるようになってもどうやってコードに落とし込むかわからない...

Slide 86

Slide 86 text

これで数字と文字列は読み取れるようになった! 読み方がわかればそんなに難しくないと思いませんか? 数字と文字列の読み方がわかれば他のデータも大体読めるようになります。 でも...読めるようになってもどうやってコードに落とし込むかわからない... 何を使えばいいのかわからない...

Slide 87

Slide 87 text

これで数字と文字列は読み取れるようになった! 読み方がわかればそんなに難しくないと思いませんか? 数字と文字列の読み方がわかれば他のデータも大体読めるようになります。 でも...読めるようになってもどうやってコードに落とし込むかわからない... 何を使えばいいのかわからない... そもそもどうやってデータを取得するのかわからない...

Slide 88

Slide 88 text

AIを使おう! 書き出しはAIに任せる

Slide 89

Slide 89 text

雑に送ってみる ``` 4a 00 00 00 ``` 上記はMySQLサーバーから送られてくるパケットの一部です。それぞれ以下要件に従ってデコードするコードをScalaで作成してください。 - Scodecライブラリを使用 - 最初の3つはペイロードの長さとしてデコード - 最後の1つはシークエンスIDとしてデコード ※ ライブラリの指定はしなくても良い (使いたいものがあれば指定)

Slide 90

Slide 90 text

生成されたコード ※ プロンプトや使用言語、その時の調子によって精度は変わります。 1 import scodec._ 2 import scodec.bits._ 3 import scodec.codecs._ 4 import scodec.Attempt._ 5 6 case class MySQLPacket( 7 payloadLength: Int, 8 sequenceId: Int 9 )

Slide 91

Slide 91 text

object MySQLPacketDecoder: // ペイロード長とシーケンスIDのコーデック定義 val payloadLengthCodec: Codec[Int] = ("payloadLength" | uint24L) val sequenceIdCodec: Codec[Int] = ("sequenceId" | uint8L) // MySQLPacket用にコーデックを1つにまとめる val mySQLPacketCodec: Codec[MySQLPacket] = ( payloadLengthCodec :: sequenceIdCodec ).as[MySQLPacket] def main(args: Array[String]): Unit = // 16進文字列をバイト列に変換 val hexString = "4a000000" val bytesOpt = ByteVector.fromHex(hexString) // ByteVectorが正常に作成されたことを確認する。 bytesOpt match case Some(bytes) => // パケットをデコードする mySQLPacketCodec.decode(bytes.bits) match case Successful(DecodeResult(packet, _)) => println(s"Payload Length: ${packet.payloadLength}") println(s"Sequence ID: ${packet.sequenceId}") case Failure(err) => println(s"Failed to decode: $err") case None => println(s"Invalid hex string: $hexString") 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

Slide 92

Slide 92 text

// ペイロード長とシーケンスIDのコーデック定義 val payloadLengthCodec: Codec[Int] = ("payloadLength" | uint24L) val sequenceIdCodec: Codec[Int] = ("sequenceId" | uint8L) 1 object MySQLPacketDecoder: 2 3 4 5 // MySQLPacket用にコーデックを1つにまとめる 6 val mySQLPacketCodec: Codec[MySQLPacket] = ( 7 payloadLengthCodec :: sequenceIdCodec 8 ).as[MySQLPacket] 9 def main(args: Array[String]): Unit = 10 // 16進文字列をバイト列に変換 11 val hexString = "4a000000" 12 val bytesOpt = ByteVector.fromHex(hexString) 13 // ByteVectorが正常に作成されたことを確認する。 14 bytesOpt match 15 case Some(bytes) => 16 // パケットをデコードする 17 mySQLPacketCodec.decode(bytes.bits) match 18 case Successful(DecodeResult(packet, _)) => 19 println(s"Payload Length: ${packet.payloadLength}") 20 println(s"Sequence ID: ${packet.sequenceId}") 21 case Failure(err) => 22 println(s"Failed to decode: $err") 23 case None => 24 println(s"Invalid hex string: $hexString")

Slide 93

Slide 93 text

// MySQLPacket用にコーデックを1つにまとめる val mySQLPacketCodec: Codec[MySQLPacket] = ( payloadLengthCodec :: sequenceIdCodec ).as[MySQLPacket] 1 object MySQLPacketDecoder: 2 // ペイロード長とシーケンスIDのコーデック定義 3 val payloadLengthCodec: Codec[Int] = ("payloadLength" | uint24L) 4 val sequenceIdCodec: Codec[Int] = ("sequenceId" | uint8L) 5 6 7 8 9 def main(args: Array[String]): Unit = 10 // 16進文字列をバイト列に変換 11 val hexString = "4a000000" 12 val bytesOpt = ByteVector.fromHex(hexString) 13 // ByteVectorが正常に作成されたことを確認する。 14 bytesOpt match 15 case Some(bytes) => 16 // パケットをデコードする 17 mySQLPacketCodec.decode(bytes.bits) match 18 case Successful(DecodeResult(packet, _)) => 19 println(s"Payload Length: ${packet.payloadLength}") 20 println(s"Sequence ID: ${packet.sequenceId}") 21 case Failure(err) => 22 println(s"Failed to decode: $err") 23 case None => 24 println(s"Invalid hex string: $hexString")

Slide 94

Slide 94 text

// 16進文字列をバイト列に変換 val hexString = "4a000000" val bytesOpt = ByteVector.fromHex(hexString) 1 object MySQLPacketDecoder: 2 // ペイロード長とシーケンスIDのコーデック定義 3 val payloadLengthCodec: Codec[Int] = ("payloadLength" | uint24L) 4 val sequenceIdCodec: Codec[Int] = ("sequenceId" | uint8L) 5 // MySQLPacket用にコーデックを1つにまとめる 6 val mySQLPacketCodec: Codec[MySQLPacket] = ( 7 payloadLengthCodec :: sequenceIdCodec 8 ).as[MySQLPacket] 9 def main(args: Array[String]): Unit = 10 11 12 13 // ByteVectorが正常に作成されたことを確認する。 14 bytesOpt match 15 case Some(bytes) => 16 // パケットをデコードする 17 mySQLPacketCodec.decode(bytes.bits) match 18 case Successful(DecodeResult(packet, _)) => 19 println(s"Payload Length: ${packet.payloadLength}") 20 println(s"Sequence ID: ${packet.sequenceId}") 21 case Failure(err) => 22 println(s"Failed to decode: $err") 23 case None => 24 println(s"Invalid hex string: $hexString")

Slide 95

Slide 95 text

// パケットをデコードする mySQLPacketCodec.decode(bytes.bits) match case Successful(DecodeResult(packet, _)) => println(s"Payload Length: ${packet.payloadLength}") println(s"Sequence ID: ${packet.sequenceId}") case Failure(err) => println(s"Failed to decode: $err") 1 object MySQLPacketDecoder: 2 // ペイロード長とシーケンスIDのコーデック定義 3 val payloadLengthCodec: Codec[Int] = ("payloadLength" | uint24L) 4 val sequenceIdCodec: Codec[Int] = ("sequenceId" | uint8L) 5 // MySQLPacket用にコーデックを1つにまとめる 6 val mySQLPacketCodec: Codec[MySQLPacket] = ( 7 payloadLengthCodec :: sequenceIdCodec 8 ).as[MySQLPacket] 9 def main(args: Array[String]): Unit = 10 // 16進文字列をバイト列に変換 11 val hexString = "4a000000" 12 val bytesOpt = ByteVector.fromHex(hexString) 13 // ByteVectorが正常に作成されたことを確認する。 14 bytesOpt match 15 case Some(bytes) => 16 17 18 19 20 21 22 23 case None => 24 println(s"Invalid hex string: $hexString")

Slide 96

Slide 96 text

object MySQLPacketDecoder: // ペイロード長とシーケンスIDのコーデック定義 val payloadLengthCodec: Codec[Int] = ("payloadLength" | uint24L) val sequenceIdCodec: Codec[Int] = ("sequenceId" | uint8L) // MySQLPacket用にコーデックを1つにまとめる val mySQLPacketCodec: Codec[MySQLPacket] = ( payloadLengthCodec :: sequenceIdCodec ).as[MySQLPacket] def main(args: Array[String]): Unit = // 16進文字列をバイト列に変換 val hexString = "4a000000" val bytesOpt = ByteVector.fromHex(hexString) // ByteVectorが正常に作成されたことを確認する。 bytesOpt match case Some(bytes) => // パケットをデコードする mySQLPacketCodec.decode(bytes.bits) match case Successful(DecodeResult(packet, _)) => println(s"Payload Length: ${packet.payloadLength}") println(s"Sequence ID: ${packet.sequenceId}") case Failure(err) => println(s"Failed to decode: $err") case None => println(s"Invalid hex string: $hexString") 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

Slide 97

Slide 97 text

実行してみると… 1 Payload Length: 74 2 Sequence ID: 0

Slide 98

Slide 98 text

実行してみると… これでデータの取得ができるようになりました! 1 Payload Length: 74 2 Sequence ID: 0

Slide 99

Slide 99 text

実行してみると… これでデータの取得ができるようになりました! 雑に投げるだけでもAIがコードを生成してくれるので、わからなくてもこれを下敷きにして進めていけばい い! 1 Payload Length: 74 2 Sequence ID: 0

Slide 100

Slide 100 text

実行してみると… これでデータの取得ができるようになりました! 雑に投げるだけでもAIがコードを生成してくれるので、わからなくてもこれを下敷きにして進めていけばい い! 今ならclaudeとか他のモデルもあるし、プロンプトを丁寧に書けばもっといいコードが生成されるかもしれな い! 1 Payload Length: 74 2 Sequence ID: 0

Slide 101

Slide 101 text

実行してみると… これでデータの取得ができるようになりました! 雑に投げるだけでもAIがコードを生成してくれるので、わからなくてもこれを下敷きにして進めていけばい い! 今ならclaudeとか他のモデルもあるし、プロンプトを丁寧に書けばもっといいコードが生成されるかもしれな い! AIにいい感じに生成してもらうために調べて伝えるということをやっていると自然と身についていく! 1 Payload Length: 74 2 Sequence ID: 0

Slide 102

Slide 102 text

実行してみると… これでデータの取得ができるようになりました! 雑に投げるだけでもAIがコードを生成してくれるので、わからなくてもこれを下敷きにして進めていけばい い! 今ならclaudeとか他のモデルもあるし、プロンプトを丁寧に書けばもっといいコードが生成されるかもしれな い! AIにいい感じに生成してもらうために調べて伝えるということをやっていると自然と身についていく! あとは同じように他の項目もデコードしていき組み合わせていけば、MySQLサーバーと通信するためのコード ができていきます。 1 Payload Length: 74 2 Sequence ID: 0

Slide 103

Slide 103 text

コードを書いても コードを書いたとしても本当に正しいのかわからない…

Slide 104

Slide 104 text

コードを書いても コードを書いたとしても本当に正しいのかわからない… AIは間違ったことを教えてくれることがある...

Slide 105

Slide 105 text

コードを書いても コードを書いたとしても本当に正しいのかわからない… AIは間違ったことを教えてくれることがある... リトルエンディアンとビッグエンディアンの違いで取得方法が間違っていてもデータの取得ができてしまう...

Slide 106

Slide 106 text

コードを書いても コードを書いたとしても本当に正しいのかわからない… AIは間違ったことを教えてくれることがある... リトルエンディアンとビッグエンディアンの違いで取得方法が間違っていてもデータの取得ができてしまう... 間違ったデータを扱っていると、どこで間違ったのかがわからない...

Slide 107

Slide 107 text

既存ライブラリでカンニングしよう!

Slide 108

Slide 108 text

カンニングとは? MySQLのような広く使われているようなデータベースは、多くのライブラリが存在しています。 今作ろうとしているものは既に誰かが作っている。 じゃあ、それを見れば実装方法はわかるよね?

Slide 109

Slide 109 text

実際にカンニングを行う 準備は何が必要か?

Slide 110

Slide 110 text

実際にカンニングを行う 準備は何が必要か? 準備はもうできている!

Slide 111

Slide 111 text

実際にカンニングを行う 準備は何が必要か? 準備はもうできている! ngrepでパケットを監視できる!

Slide 112

Slide 112 text

実際にカンニングを行う 準備は何が必要か? 準備はもうできている! ngrepでパケットを監視できる! パケットの見方も大体わかる!

Slide 113

Slide 113 text

実際にカンニングを行う 準備は何が必要か? 準備はもうできている! ngrepでパケットを監視できる! パケットの見方も大体わかる! MySQLサーバーを起動して、既存ライブラリとの通信内容を確認すればいい!

Slide 114

Slide 114 text

何をみるのか? どのような順番でデータがやり取りされているか どのようにエンコードしてサーバーにデータを送信すれば良いか etc… 私は主に以下の接続をそれぞれ監視して、どのようなデータがやり取りされているかを確認しました。 mysql client mysql-connector-java 自分の作ってみたい言語のライブラリを使えばOk!

Slide 115

Slide 115 text

まずは ScalaはJDBCを使ってMySQLサーバーと通信するので、mysql-connector-javaを使って通信しているデータを見 てみる。 まずはここから! ※ Scala CLIを使用 1 //> using scala "3.3.3" 2 //> using dep mysql:mysql-connector-java:8.0.33 3 4 val dataSource = new MysqlDataSource() 5 dataSource.setServerName("127.0.0.1") 6 dataSource.setPortNumber(3306) 7 dataSource.setUser("username") 8 dataSource.setPassword("password") 9 dataSource.setUseSSL(false) // v8 (caching_sha2_password) 使用の場合 10 dataSource.setAllowPublicKeyRetrieval(true) // v8 (caching_sha2_password) 使用の場合 11 12 // 接続 13 val connection = dataSource.getConnection 1 scala-cli mysql-connector.sc

Slide 116

Slide 116 text

先ほどと同じようにngrepでパケットを監視してみると… 先ほどと同じ形式でデータが送られてきていることがわかる 1 T 127.0.0.1:13306 -> 127.0.0.1:59433 [AP] #5 2 4a 00 00 00 0a 38 2e 30 2e 33 33 00 79 0a 00 00 J....8.0.33.y... 3 4b 3d 5f 7f 66 71 40 68 00 ff ff ff 02 00 ff df K=_.fq@h.���..�� 4 15 00 00 00 00 00 00 00 00 00 00 54 45 45 19 59 ...........TEE.Y 5 63 0f 63 18 27 76 07 00 63 61 63 68 69 6e 67 5f c.c.'v..caching_ 6 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 sha2_password.

Slide 117

Slide 117 text

送られたデータに対して、MySQLドライバーはこのようなデータを送信していることがわかる つまり… 1 T 127.0.0.1:59433 -> 127.0.0.1:13306 [AP] #7 2 e0 00 00 01 07 a2 3e 19 ff ff ff 00 ff 00 00 00 �....�>.���.�... 3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 4 00 00 00 00 6c 64 62 63 00 20 1c ea ae b9 6a bd ....ldbc. .ꮹj� 5 3c cd 06 dc 21 7a 98 53 6c 6b 6e 82 49 bc d7 44 <�.�!z.Slkn.I��D 6 d1 2d 42 7c f7 28 f5 f5 61 5a 63 61 63 68 69 6e �-B|�(��aZcachin 7 67 5f 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 g_sha2_password. 8 83 10 5f 72 75 6e 74 69 6d 65 5f 76 65 72 73 69 .._runtime_versi 9 6f 6e 07 31 31 2e 30 2e 31 37 0f 5f 63 6c 69 65 on.11.0.17._clie 10 6e 74 5f 76 65 72 73 69 6f 6e 06 38 2e 30 2e 33 nt_version.8.0.3 11 33 0f 5f 63 6c 69 65 6e 74 5f 6c 69 63 65 6e 73 3._client_licens 12 65 03 47 50 4c 0f 5f 72 75 6e 74 69 6d 65 5f 76 e.GPL._runtime_v 13 65 6e 64 6f 72 0f 41 6d 61 7a 6f 6e 2e 63 6f 6d endor.Amazon.com 14 20 49 6e 63 2e 0c 5f 63 6c 69 65 6e 74 5f 6e 61 Inc.._client_na 15 6d 65 11 4d 79 53 51 4c 20 43 6f 6e 6e 65 63 74 me.MySQL Connect 16 6f 72 2f 4a or/J

Slide 118

Slide 118 text

送られたデータに対して、MySQLドライバーはこのようなデータを送信していることがわかる つまり… これが答え! 1 T 127.0.0.1:59433 -> 127.0.0.1:13306 [AP] #7 2 e0 00 00 01 07 a2 3e 19 ff ff ff 00 ff 00 00 00 �....�>.���.�... 3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 4 00 00 00 00 6c 64 62 63 00 20 1c ea ae b9 6a bd ....ldbc. .ꮹj� 5 3c cd 06 dc 21 7a 98 53 6c 6b 6e 82 49 bc d7 44 <�.�!z.Slkn.I��D 6 d1 2d 42 7c f7 28 f5 f5 61 5a 63 61 63 68 69 6e �-B|�(��aZcachin 7 67 5f 73 68 61 32 5f 70 61 73 73 77 6f 72 64 00 g_sha2_password. 8 83 10 5f 72 75 6e 74 69 6d 65 5f 76 65 72 73 69 .._runtime_versi 9 6f 6e 07 31 31 2e 30 2e 31 37 0f 5f 63 6c 69 65 on.11.0.17._clie 10 6e 74 5f 76 65 72 73 69 6f 6e 06 38 2e 30 2e 33 nt_version.8.0.3 11 33 0f 5f 63 6c 69 65 6e 74 5f 6c 69 63 65 6e 73 3._client_licens 12 65 03 47 50 4c 0f 5f 72 75 6e 74 69 6d 65 5f 76 e.GPL._runtime_v 13 65 6e 64 6f 72 0f 41 6d 61 7a 6f 6e 2e 63 6f 6d endor.Amazon.com 14 20 49 6e 63 2e 0c 5f 63 6c 69 65 6e 74 5f 6e 61 Inc.._client_na 15 6d 65 11 4d 79 53 51 4c 20 43 6f 6e 6e 65 63 74 me.MySQL Connect 16 6f 72 2f 4a or/J

Slide 119

Slide 119 text

MySQLドライバーの返答に対して、MySQLサーバーはこのようなデータを返している つまり… 1 T 127.0.0.1:13306 -> 127.0.0.1:59433 [AP] #9 2 02 00 00 02 01 03 ...... 3 4 T 127.0.0.1:13306 -> 127.0.0.1:59433 [AP] #11 5 07 00 00 03 00 00 00 02 00 00 00 ........... 6 7 ...

Slide 120

Slide 120 text

MySQLドライバーの返答に対して、MySQLサーバーはこのようなデータを返している つまり… ここでハンドシェイクが完了していることがわかる! ※ MySQLでは正常系の形式が決まっておりこれはその正常系の形式 1 T 127.0.0.1:13306 -> 127.0.0.1:59433 [AP] #9 2 02 00 00 02 01 03 ...... 3 4 T 127.0.0.1:13306 -> 127.0.0.1:59433 [AP] #11 5 07 00 00 03 00 00 00 02 00 00 00 ........... 6 7 ...

Slide 121

Slide 121 text

MySQLドライバーの返答に対して、MySQLサーバーはこのようなデータを返している つまり… ここでハンドシェイクが完了していることがわかる! ※ MySQLでは正常系の形式が決まっておりこれはその正常系の形式 まずはMySQLサーバーからこれが帰ってくるのを目指す! 1 T 127.0.0.1:13306 -> 127.0.0.1:59433 [AP] #9 2 02 00 00 02 01 03 ...... 3 4 T 127.0.0.1:13306 -> 127.0.0.1:59433 [AP] #11 5 07 00 00 03 00 00 00 02 00 00 00 ........... 6 7 ...

Slide 122

Slide 122 text

送られたデータに対して、MySQLドライバーはこのようなデータ を送信していることがわかる 送信するパケットの形式も決まっており、ドキュメン トに記載されている Protocol::HandshakeResponse 読み方はMySQLサーバーから受け取るパケットの形式 と同じ! ※ 一部のみ表示

Slide 123

Slide 123 text

先ほどと同じような手法をとっていく 1. パケットの形式を確認 2. AIを使ってコードを生成 これを繰り返していくことで、MySQLサーバーと通信するためのコードができていく!

Slide 124

Slide 124 text

作って良かったこと、変わったこと

Slide 125

Slide 125 text

良かったこと データベースをわかった気になれる データベースクライアントを作ることでデータベースの通信周りの仕組みがわかる JDBC(自分の普段触っている言語のデータベース周り)に詳しくなった気になれる MySQLサーバーと通信していると思っていたメソッドが実は通信していなかったり 自分の想定していた処理と違っていたり etc… OSSのコントリビュートに挑戦できるようになる 既存のデータベースクライアントのバグを見つけて修正できるようになる どんな処理でもパフォーマンスを考えるようになった (これがなんとなくわかるようになった) 適当に書くとものすごく遅くなる

Slide 126

Slide 126 text

良かったこと データベースをわかった気になれる データベースクライアントを作ることでデータベースの通信周りの仕組みがわかる JDBC(自分の普段触っている言語のデータベース周り)に詳しくなった気になれる MySQLサーバーと通信していると思っていたメソッドが実は通信していなかったり 自分の想定していた処理と違っていたり etc… OSSのコントリビュートに挑戦できるようになる 既存のデータベースクライアントのバグを見つけて修正できるようになる どんな処理でもパフォーマンスを考えるようになった (これがなんとなくわかるようになった) 適当に書くとものすごく遅くなる 単純な話自信がつく

Slide 127

Slide 127 text

良かったこと データベースをわかった気になれる データベースクライアントを作ることでデータベースの通信周りの仕組みがわかる JDBC(自分の普段触っている言語のデータベース周り)に詳しくなった気になれる MySQLサーバーと通信していると思っていたメソッドが実は通信していなかったり 自分の想定していた処理と違っていたり etc… OSSのコントリビュートに挑戦できるようになる 既存のデータベースクライアントのバグを見つけて修正できるようになる どんな処理でもパフォーマンスを考えるようになった (これがなんとなくわかるようになった) 適当に書くとものすごく遅くなる 単純な話自信がつく 自信がついてくると、他のことにも挑戦しやすくなる

Slide 128

Slide 128 text

変わったこと 何かエラーが起きたらパケットのやり取りを見にいくようになった Errorに出てくるクラスを見ると処理の流れがなんとなくイメージできるようになった データベースがシンプルに見えるようになった 目に見えなくてイメージしにくかった データベースのクライアントとライブラリのコードを読めるようになった (触ったことない言語でも) 言語によってパケットの処理方法が違っていて面白い データベースのリリースノートなどの更新内容を前よりも理解しながら読めるようになった 変更箇所がコードにどのような影響を及ぼすかをイメージしやすくなった

Slide 129

Slide 129 text

今後やりたいこと 他のデータベースのクライアントを作ってみたい (JDBCは共通のインターフェースを提供しているため同じような感じで…) データベースそのものを作ってみたい 新しい言語でデータベースクライアントを作ってみたい 時間の関係で実際のパケット受信、送信までの説明はできなかった… 今回は通信の監視方法、パケットの見方をメインに話しましたが、次回があって需要があればパケット送信の 仕方、データの処理方法などを話していきたい

Slide 130

Slide 130 text

まとめ データベースクライアントを作る時は…

Slide 131

Slide 131 text

まとめ データベースクライアントを作る時は… 1. データベースとの通信を見ましょう

Slide 132

Slide 132 text

まとめ データベースクライアントを作る時は… 1. データベースとの通信を見ましょう 2. AIを使ってコードを生成しましょう

Slide 133

Slide 133 text

まとめ データベースクライアントを作る時は… 1. データベースとの通信を見ましょう 2. AIを使ってコードを生成しましょう 3. 既存ライブラリの通信をカンニングしましょう

Slide 134

Slide 134 text

まとめ データベースクライアントを作る時は… 1. データベースとの通信を見ましょう 2. AIを使ってコードを生成しましょう 3. 既存ライブラリの通信をカンニングしましょう 4. 1 ~ 3を繰り返しましょう

Slide 135

Slide 135 text

まとめ データベースクライアントを作る時は… 1. データベースとの通信を見ましょう 2. AIを使ってコードを生成しましょう 3. 既存ライブラリの通信をカンニングしましょう 4. 1 ~ 3を繰り返しましょう どんなことでもコツコツ積み上げて行けば大きなものができていく!

Slide 136

Slide 136 text

まとめ データベースクライアントを作る時は… 1. データベースとの通信を見ましょう 2. AIを使ってコードを生成しましょう 3. 既存ライブラリの通信をカンニングしましょう 4. 1 ~ 3を繰り返しましょう どんなことでもコツコツ積み上げて行けば大きなものができていく! 興味が湧いた人は挑戦してみてね!

Slide 137

Slide 137 text

ご清聴ありがとうございました!