github.com/XiaoMi/Gaea@v1.2.5/backend/direct_connection_en.md (about)

     1  # Direct Connection in Backend package
     2  
     3  ## Code Explanation
     4  
     5  ### The first step
     6  
     7  > The first step is to send the initial handshake packet from MariaDB to Gaea.
     8  
     9  There are some details about the initial handshake packet in [the official document](https://mariadb.com/kb/en/connection/) , and please see the details below.
    10  
    11  <img src="./assets/image-20220315221559157.png" alt="image-20220315221559157" style="zoom:100%;" /> 
    12  
    13  The actual packet demonstrates how this handshake works, and please see details below.
    14  
    15  | packet                          | exmaple                                                      |
    16  | ------------------------------- | ------------------------------------------------------------ |
    17  | int<1> protocol version         | Protocol Version 10                                          |
    18  | string<NUL> server version      | MariaDB version is <br /><br />[]uint8{<br />53, 46, 53, 46, 53,<br />45, 49, 48, 46, 53,<br />46, 49, 50, 45, 77,<br />97, 114, 105, 97, 68,<br />66, 45, 108, 111, 103<br />}<br /><br />Converting the array to ASCII, the result is "5.5.5-10.5.12-MariaDB-log". |
    19  | int<4> connection id            | Connection ID is []uint8{16, 0, 0, 0}.<br /><br />After reversing the array, it becomes []uint8{0, 0, 0, 16} that equals to []uint32{16}. |
    20  | string<8> scramble 1st part     | The first part of the scramble:<br /><br />MariaDB utilizes the scramble for secure password authentication.<br /><br />The scramble is 20 bytes of data; the first part occupies 8 bytes, []uint8{81, 64, 43, 85, 76, 90, 97, 91}. |
    21  | string<1> reserved byte         | It occupies 1 byte, []uint8{0}.                              |
    22  | int<2> server capabilities      | The first part of the capability occupies 2 bytes,  []uint8{254, 247}. |
    23  | int<1> server default collation | The charset of MariaDB in the current exameple is 33.<br /><br />After checking<br />[document](https://mariadb.com/kb/en/supported-character-sets-and-collations/) <br />or<br />using a command "SHOW CHARACTER SET LIKE 'utf8'",<br />finding out that number 33 means "utf8_general_ci". |
    24  | int<2> status flags             | The status of MariaDB in the current exameple is []uint8{2, 0}.<br/><br />Reversing from the status flags to []uint8{0, 2} and then converting them to binary, []uint{0b000000000, 0b00000010}.<br /><br />After referring to "Gaea/mysql/constants.go", the result means "Autocommit." |
    25  | int<2> server capabilities      | The second part of the capability occupies 2 bytes,  []uint8{255, 129}. |
    26  
    27  Calculate the whole capability
    28  
    29  ```
    30  Gathering two parts of the capability and combining them, the result is []uint8{254, 247, 255, 129}.
    31  
    32  After Converting the result to binary, the result becomes []uint8{0b10000001, 0b11111111, 0b11110111, 0b11111110}.
    33  
    34  After that, refer to [the website](https://mariadb.com/kb/en/connection/) and ensure some details without difficulty.
    35  
    36  For example, the first element of the capability is 0, which means the packet came from MariaDB to Gaea.
    37  ```
    38  
    39  The next table follows on from the previous one.
    40  
    41  | item    | value                                                        |
    42  | ------- | ------------------------------------------------------------ |
    43  | packet  | if (server_capabilities & PLUGIN_AUTH)<br/>        int<1> plugin data length <br/>    else<br/>        int<1> 0x00 |
    44  | example | skip 1 byte                                                  |
    45  
    46  The next table follows on from the previous one.
    47  
    48  | item    | value            |
    49  | ------- | ---------------- |
    50  | packet  | string<6> filler |
    51  | example | skip 6 bytes     |
    52  
    53  The next table follows on from the previous one.
    54  
    55  | item    | value                                                        |
    56  | ------- | ------------------------------------------------------------ |
    57  | packet  | if (server_capabilities & CLIENT_MYSQL)<br/>        string<4> filler <br/>    else<br/>        int<4> server capabilities 3rd part .<br />        MariaDB specific flags /* MariaDB 10.2 or later */ |
    58  | example | skip 4 bytes                                                 |
    59  
    60  The next table follows on from the previous one.
    61  
    62  | item    | value                                                        |
    63  | ------- | ------------------------------------------------------------ |
    64  | packet  | if (server_capabilities & CLIENT_SECURE_CONNECTION)<br/>        string<n> scramble 2nd part . Length = max(12, plugin data length - 9)<br/>        string<1> reserved byte |
    65  | example | The scramble is 20 bytes of data; the second part occupies 12 (20-8=12) bytes, []uint8{34, 53, 36, 85, 93, 86, 117, 105, 49, 87, 65, 125}. |
    66  
    67  The next table follows on from the previous one.
    68  
    69  | item    | value                                                        |
    70  | ------- | ------------------------------------------------------------ |
    71  | packet  | if (server_capabilities & PLUGIN_AUTH)<br/>        string<NUL> authentication plugin name |
    72  | example | Gaea discards the rest of the data in the packet because there is no use for "PLUGIN_AUTH". |
    73  
    74  combine the whole data of the scramble:
    75  
    76  ```
    77  The first part of the scramble is []uint8{81, 64, 43, 85, 76, 90, 97, 91}
    78  The second part of the scramble is []uint8{34, 53, 36, 85, 93, 86, 117, 105, 49, 87, 65, 125}
    79  
    80  After combining them, the final result is []uint8{81, 64, 43, 85, 76, 90, 97, 91, 34, 53, 36, 85, 93, 86, 117, 105, 49, 87, 65, 125}.
    81  ```
    82  
    83  ### The second step
    84  
    85  > The second step is to calculate the auth base on the scramble, combined with two parts of the scramble.
    86  
    87  There are some details about the auth formula in [the official document](https://dev.mysql.com/doc/internals/en/secure-password-authentication.html) .
    88  
    89  ```
    90  some formulas for the auth:
    91  
    92  SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )
    93      stage1 = SHA1( password )
    94      stage1Hash = SHA1( stage1 ) = SHA1( SHA1( password ) )
    95      scramble = SHA1( scramble <concat> SHA1( stage1Hash ) ) // the first new scramble
    96      scramble = stage1 XOR scramble // the second new scramble
    97  ```
    98  
    99  Assume
   100  
   101  - The password for a secure login process in MariaDB is 12345.
   102  - The auth base on the scramble, combined with two parts of the scramble, is []uint8{81, 64, 43, 85, 76, 90, 97, 91, 34, 53, 36, 85, 93, 86, 117, 105, 49, 87, 65, 125}.
   103    The result that converted from decimal to hexadecimal is []uint8{51, 40, 2B, 55, 4c, 5a, 61, 5b, 22, 35, 24, 55, 5d, 56,  75,  69, 31, 57, 41,  7d}.
   104    It is the same as  51402B554c5A615b223524555d5675693157417d.
   105  
   106  Regarding the stage1 formula, Linux Bash calculates the result and compares it.
   107  
   108  ```bash
   109  # stage1 = SHA1( password )
   110  
   111  # calculate stage1
   112  $ echo -n 12345 | sha1sum | head -c 40 # convert password 12345 to stage1
   113  8cb2237d0679ca88db6464eac60da96345513964 # stage1
   114  ```
   115  
   116  As regards the stage1Hash formula, Linux Bash calculates the result and compares it.
   117  
   118  ```bash
   119  # stage1Hash = SHA1( stage1 ) = SHA1( SHA1( password ) )
   120  
   121  $ echo -n 12345 | sha1sum | xxd -r -p | sha1sum | head -c 40
   122  00a51f3f48415c7d4e8908980d443c29c69b60c9 # stage1hash
   123  
   124  $ echo -n 8cb2237d0679ca88db6464eac60da96345513964 | xxd -r -p | sha1sum | head -c 40
   125  00a51f3f48415c7d4e8908980d443c29c69b60c9 # stage1hash
   126  ```
   127  
   128  Linux Bash concatenates the scramble and stage1Hash into the string concat.
   129  
   130  ```bash
   131  # scramble is 51402B554c5A615b223524555d5675693157417d, the first half part.
   132  # stage1Hash is 00a51f3f48415c7d4e8908980d443c29c69b60c9, the second half part.
   133  
   134  # calculate "20-bytes random data from server" <concatenate> SHA1( SHA1( password ) )
   135  $ echo -n 51402B554c5A615b223524555d5675693157417d 00a51f3f48415c7d4e8908980d443c29c69b60c9 |  sed "s/ //g"
   136  51402B554c5A615b223524555d5675693157417d00a51f3f48415c7d4e8908980d443c29c69b60c9 # concat
   137  ```
   138  
   139  In terms of the first new scramble, Linux Bash calculates the result and compares it.
   140  
   141  ```bash
   142  # scramble = SHA1( concat ) = SHA1( scramble <concatenate> SHA1( stage1Hash ) )
   143  
   144  $ echo -n 51402B554c5A615b223524555d5675693157417d00a51f3f48415c7d4e8908980d443c29c69b60c9 | xxd -r -p | sha1sum | head -c 40
   145  0ca0f764a59d1cdb10a87f0155d61aa54be1c71a # The first new scramble
   146  ```
   147  
   148  In the case of the second new scramble, Linux Bash calculates the result and compares it.
   149  
   150  ```bash
   151  # scramble = stage1 XOR scramble
   152  
   153  $ stage1=0x8cb2237d0679ca88db6464eac60da96345513964 # stage1
   154  $ scramble=0x0ca0f764a59d1cdb10a87f0155d61aa54be1c71a # The first new scramble
   155  $ echo $(( $stage1^$scramble ))
   156  -7792437067003134338 # insufficient precision
   157  
   158  $ stage1=0x8cb2237d0679ca88db6464eac60da96345513964
   159  $ scramble=0x0ca0f764a59d1cdb10a87f0155d61aa54be1c71a
   160  $ printf "0x%X" $(( (($stage1>>40)) ^ (($scrambleFirst>>40)) ))
   161  0xFFFFFFFFFF93DBB3 # insufficient precision
   162  
   163  # Linux Bash divides stage1 and the first new scramble into four parts and individually makes four bitwise XOR operations.
   164  $ printf "0x%X" $(( ((0x8cb2237d06)) ^ ((0x0ca0f764a5)) ))
   165  $ printf "%X" $(( ((0x79ca88db64)) ^ ((0x9d1cdb10a8)) ))
   166  $ printf "%X" $(( ((0x64eac60da9)) ^ ((0x7f0155d61a)) ))
   167  $ printf "%X" $(( ((0x6345513964)) ^ ((0xa54be1c71a)) ))
   168  0x8012D419A3E4D653CBCC1BEB93DBB3C60EB0FE7E # correct
   169  
   170  # scramble is []uint8{ 80, 12,  D4, 19,  A3,  E4,  D6, 53,  CB,  CC, 1B,  EB,  93,  DB,  B3,  C6, 0E,  B0,  FE,  7E} // hexadecimal
   171  # decimal
   172  # scramble 为 []uint8{128, 18, 212, 25, 163, 228, 214, 83, 203, 204, 27, 235, 147, 219, 179, 198, 14, 176, 254, 126} // the same as the result in Gaea
   173  ```
   174  
   175  The correct result, auth, is the same as Gaea's.
   176  
   177  <img src="./assets/image-20220318183833245.png" alt="image-20220318183833245" style="zoom:70%;" /> 
   178  
   179  ### The third step
   180  
   181  > The second step is to reply to MariaDB after receiving the initial handshake packet.
   182  
   183  There are some details about the response packet in [the official document](https://mariadb.com/kb/en/connection/) , and please see the details below.
   184  
   185  <img src="./assets/image-20220318083633693.png" alt="image-20220318083633693" style="zoom:100%;" /> 
   186  
   187  The actual packet demonstrates how this response works, and please see details below.
   188  
   189  | capability                   | binary             | decimal |
   190  | ---------------------------- | ------------------ | ------- |
   191  | mysql.ClientProtocol41       | 0b0000001000000000 | 512     |
   192  | mysql.ClientSecureConnection | 0b1000000000000000 | 32768   |
   193  | mysql.ClientLongPassword     | 0b0000000000000001 | 1       |
   194  | mysql.ClientTransactions     | 0b0010000000000000 | 8192    |
   195  | mysql.ClientLongFlag         | 0b0000000000000100 | 4       |
   196  |                              |                    |         |
   197  | sum                          |                    |         |
   198  | Gaea's capability            | 0b1010001000000101 | 41477   |
   199  
   200  Calculate the new capability that coordinates with MariaDB's.
   201  
   202  ```
   203  With regard to the first step, the dc object's capability is 0b10000001111111111111011111111110, which equals 2181036030 in decimal.
   204  
   205  Obviously, it does not support "mysql.ClientLongPassword".
   206  
   207  SpeedCrunch calculator takes both Gaea's capability and the capability to make a bitwise AND operation. The result is the mutual capability.
   208  Gaea's capability & dc.capability = []uint32{41477} & []uint32{2181036030} = []uint32{41476}
   209  ```
   210  
   211  <img src="./assets/image-20220319002738908.png" alt="image-20220319002738908" style="zoom:70%;" /> 
   212  
   213  | packet                            | exmaple                                                      |
   214  | --------------------------------- | ------------------------------------------------------------ |
   215  | int<4> client capabilities        | Gaea reverses from the mutual capability []uint32{41476} to []uint8{4, 162, 0, 0} when sending the packet to MariaDB.<br /><img src="./assets/image-20220319113026919.png" alt="image-20220319113026919" style="zoom:50%;" /> |
   216  | int<4> max packet size            | It occupies 4 bytes, []uint8{0, 0, 0, 0}.                    |
   217  | int<1> client character collation | After checking [document](https://mariadb.com/kb/en/supported-character-sets-and-collations/) , finding out that number 46 means "utf8mb4_bin". |
   218  | string<19> reserved               | It occupies 19 bytes,  []uint8{<br />                                                       0, 0, 0, 0, 0,<br />                                                       0, 0, 0, 0, 0,<br />                                                       0, 0, 0, 0, 0,<br />                                                       0, 0, 0, 0,<br />                                                   } |
   219  
   220  The next table follows on from the previous one.
   221  
   222  | item    | value                                                        |
   223  | ------- | ------------------------------------------------------------ |
   224  | packet  | if not (server_capabilities & CLIENT_MYSQL)<br/>    int<4> extended client capabilities <br/>else<br/>    string<4> reserved |
   225  | example | **CLIENT_MYSQL** means this packet belongs to Gaea, True.<br /><br />It occupies 4 bytes, []uint8{0, 0, 0, 0}, because the formula, <br />**not (server_capabilities & CLIENT_MYSQL)**<br />, is False. |
   226  
   227  The next table follows on from the previous one.
   228  
   229  | item    | value                                                        |
   230  | ------- | ------------------------------------------------------------ |
   231  | packet  | string<NUL> username                                         |
   232  | example | xiaomi is an account to log in MariaDB. However, it needs one byte to terminate it.<br />It will occupy 7 bytes, []uint8{120, 105, 97, 111, 109, 105, 0} |
   233  
   234  With respect to the second new scramble, Linux Bash calculates the result and compares it.
   235  
   236  ```bash
   237  $ echo -n xiaomi | od -td1
   238  0000000  120  105   97  111  109  105
   239  0000006
   240  ```
   241  
   242  The next table follows on from the previous one.
   243  
   244  | item    | value                                                        |
   245  | ------- | ------------------------------------------------------------ |
   246  | packet  | if (server_capabilities & PLUGIN_AUTH_LENENC_CLIENT_DATA)<br/>    string<lenenc> authentication data <br/>else if (server_capabilities & CLIENT_SECURE_CONNECTION)<br/>    int<1> length of authentication response<br/>    string<fix> authentication response (length is indicated by previous field) <br/>else<br/>    string<NUL> authentication response null ended |
   247  | example | Gaea supports CLIENT_SECURE_CONNECTION in this case.<br /><br />In the second step, the auth is []uint8{128, 18, 212, 25, 163, 228, 214, 83, 203, 204, 27, 235, 147, 219, 179, 198, 14, 176, 254, 126}.<br />However, Gaea has to send the length of the auth first.<br /><br />It will occupy 21 bytes, []uint8{20, 128, 18, 212, 25, 163, 228, 214, 83, 203, 204, 27, 235, 147, 219, 179, 198, 14, 176, 254, 126} |
   248  
   249  The next table follows on from the previous one.
   250  
   251  | item    | value                                                        |
   252  | ------- | ------------------------------------------------------------ |
   253  | packet  | if (server_capabilities & CLIENT_CONNECT_WITH_DB)<br/>    string<NUL> default database name |
   254  | example | Gaea deals with capabilities that are **mysql.ClientProtocol41**, **mysql.ClientSecureConnection**, **mysql.ClientTransactions**, and **mysql.ClientLongFlag** in the dc connection. <br /><br />Gaea ignores this one. |
   255  
   256  The next table follows on from the previous one.
   257  
   258  | item    | value                                                        |
   259  | ------- | ------------------------------------------------------------ |
   260  | packet  | if (server_capabilities & CLIENT_PLUGIN_AUTH)<br/>    string<NUL> authentication plugin name |
   261  | example | Gaea deals with capabilities that are **mysql.ClientProtocol41**, **mysql.ClientSecureConnection**, **mysql.ClientTransactions**, and **mysql.ClientLongFlag** in the dc connection. <br /><br />Gaea ignores this one. |
   262  
   263  The next table follows on from the previous one.
   264  
   265  | item    | value                                                        |
   266  | ------- | ------------------------------------------------------------ |
   267  | packet  | if (server_capabilities & CLIENT_CONNECT_ATTRS)<br/>    int<lenenc> size of connection attributes<br/>    while packet has remaining data<br/>        string<lenenc> key<br/>        string<lenenc> value |
   268  | example | Gaea deals with capabilities that are **mysql.ClientProtocol41**, **mysql.ClientSecureConnection**, **mysql.ClientTransactions**, and **mysql.ClientLongFlag** in the dc connection. <br /><br />Gaea ignores this one. |
   269  
   270  ## Testing
   271  
   272  > There is an explanation of what I consider about in Unit Test.
   273  
   274  ### Considering about Anonymous Function
   275  
   276  The function of the unit test whose name is "response after handshake," containing an anonymous function.
   277  
   278  The variables in the anonymous function inside the test will take the address of other variables and bring them inside the function.
   279  
   280  I consider about it again and again. It seems correct.
   281  
   282  The problem that I am concerned about is that the anonymous function takes the wrong values.
   283  
   284  ```go
   285  	// 交握第二步 Step2
   286  	t.Run("Response after Handshake", func(t *testing.T) {
   287  		var connForSengingMsgToMariadb = mysql.NewConn(mockGaea.GetConnWrite())
   288  		dc.conn = connForSengingMsgToMariadb
   289  		dc.conn.StartWriterBuffering()
   290          
   291  		customFunc := func() {
   292  			err := dc.writeHandshakeResponse41()
   293  			require.Equal(t, err, nil)
   294  			err = dc.conn.Flush()
   295  			require.Equal(t, err, nil)
   296  			err = mockGaea.GetConnWrite().Close()
   297  			require.Equal(t, err, nil)
   298  		}
   299  
   300  		fmt.Println(mockGaea.CustomSend(customFunc).ArrivedMsg(mockMariaDB))
   301  	})
   302  ```
   303  
   304  ## Check Result
   305  
   306  The result is hexadecimal when I use the Linux command and tools on other websites to calculate sha1sum.
   307  
   308  However, the IDE tool produces the result in decimal.
   309  
   310  ### Linux command
   311  
   312  Linux Bash generates the sha1shum.
   313  
   314  <img src="./assets/image-20220314214316673.png" alt="image-20220314214316673" style="zoom:80%;" /> 
   315  
   316  ### Website
   317  
   318  The tool on [the website](https://coding.tools/tw/sha1) calculates the sha1shum.
   319  
   320  <img src="./assets/image-20220314215924425.png" alt="image-20220314215924425" style="zoom:80%;" /> 
   321  
   322  ### Broken Point
   323  
   324  The IDE uses broken point to take a look at the stage1 variable.
   325  
   326  <img src="./assets/image-20220314220921338.png" alt="image-20220314220921338" style="zoom:100%;" /> 
   327  
   328  ### Comparison
   329  
   330  The table below compares the two results.
   331  
   332  One comes from taking broken point, and the other comes from tools on the website.
   333  
   334  Thus I  am sure the result is correct.
   335  
   336  | Position |  Binary  | Decimal | Hexadecimal |
   337  | :------: | :------: | :-----: | :---------: |
   338  |    0     | 10001100 |   140   |     8c      |
   339  |    1     | 10110010 |   178   |     b2      |
   340  |    2     | 00100011 |   35    |     23      |
   341  |    3     | 01111101 |   125   |     7d      |
   342  |    4     | 00000110 |    6    |     06      |
   343  |    5     | 01111001 |   121   |     79      |
   344  |    6     | 11001010 |   202   |     ca      |
   345  |    7     | 10001000 |   136   |     88      |
   346  |    8     | 11011011 |   219   |     db      |
   347  |    9     | 01100100 |   100   |     64      |
   348  |    10    | 01100100 |   100   |     64      |
   349  |    11    | 11101010 |   234   |     ea      |
   350  |    12    | 11000110 |   198   |     c6      |
   351  |    13    | 00001101 |   13    |     0d      |
   352  |    14    | 10101001 |   169   |     a9      |
   353  |    15    | 01100011 |   99    |     63      |
   354  |    16    | 01000101 |   69    |     45      |
   355  |    17    | 01010001 |   81    |     51      |
   356  |    18    | 00111001 |   57    |     39      |
   357  |    19    | 01100100 |   100   |     64      |