├── COPYING ├── LICENSE ├── README ├── README.md ├── config ├── pairs.c ├── pairs.h ├── password.c ├── password.h ├── protocol.c ├── protocol.h └── tc_mysql_module.c /COPYING: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 NetEase, Inc. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 NetEase, Inc. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A TCPCopy module for MySQL Replay 2 | 3 | The mysql-replay-module is a TCPCopy extension designed to replay MySQL sessions, enabling real-world testing of MySQL applications. 4 | 5 | For detailed information, please consult [TCPCopy](https://github.com/session-replay-tools/tcpcopy) before proceeding. 6 | 7 | ## Installation 8 | 9 | ### Installing `intercept` on the Assistant Server 10 | 11 | 1. git clone git://github.com/session-replay-tools/intercept.git 12 | 2. cd intercept 13 | 3. ./configure --with-resp-payload 14 | 4. make 15 | 5. make install 16 | 17 | ### Installing `tcpcopy` on the Online Server 18 | 19 | 1. git clone git://github.com/session-replay-tools/tcpcopy.git 20 | 2. cd tcpcopy 21 | 3. git clone git://github.com/session-replay-tools/mysql-replay-module.git 22 | 4. ./configure --set-protocol-module=mysql-replay-module 23 | 5. make 24 | 6. make install 25 | 26 | ## Usage Guide 27 | 28 | ### 1. **On the Target Server Running MySQL Applications:** 29 | 30 | Configure the route commands to direct response packets to the assistant server. For example, assuming `10.110.12.18` is the IP address of the assistant server and `10.110.12.15` is the MySQL client IP address, use the following route command to direct all responses from `10.110.12.15` to the assistant server: 31 | 32 | `route add -host 10.110.12.15 gw 10.110.12.18` 33 | 34 | ### 2. **On the Assistant Server Running `intercept` (Root Privilege or CAP_NET_RAW Capability Required):** 35 | 36 | `./intercept -F -i ` 37 | 38 | Note that the filter format is the same as the pcap filter. For example: 39 | 40 | `./intercept -i eth0 -F 'tcp and src port 3306' -d` 41 | 42 | In this example, `intercept` will capture response packets from a TCP-based application listening on port 3306, using the eth0 network device. 43 | 44 | Please note that `ip_forward` is not enabled on the assistant server. 45 | 46 | ### 3. **On the Online Source Server (Root Privilege or CAP_NET_RAW Capability Required):** 47 | 48 | a) Configure user password pairs in `conf/plugin.conf` within the installation directory. 49 | 50 | Format: 51 | 52 | `user user1@password1,user2@password2,...,userN@passwordN;` 53 | 54 | For example: 55 | 56 | `user root@123456;` 57 | 58 | b) To start `tcpcopy`, use the following command: 59 | 60 | `./tcpcopy -x localServerPort-targetServerIP:targetServerPort -s [-c ]` 61 | 62 | For example (assuming 10.110.12.17 is the IP address of the target server): 63 | 64 | `./tcpcopy -x 3306-10.110.12.17:3306 -s 10.110.12.18 -c 10.110.12.15` 65 | 66 | `tcpcopy` captures MySQL packets (assuming MySQL listens on port 3306) on the current server, modifies all client IP addresses to 10.110.12.15, and forwards them to port 3306 on `10.110.12.17` (the target MySQL server). It also connects to `10.110.12.18` to request that `intercept` forwards the response packets to it. 67 | 68 | ## Note 69 | 70 | 1. User Accounts and Privileges: Both MySQL instances on the target and online servers must have identical user accounts and privileges, though passwords can differ. 71 | 2. Session Replay: Only complete sessions can be replayed. 72 | 3. OpenSSL Support: OpenSSL 1.1.0+ is not currently supported. 73 | 4. Password Plugin Compatibility: MySQL 8.0’s **caching_sha2_password** is not supported. To test MySQL 8.0 using MySQL 5.7 production flows, use **mysql_native_password** and ensure that all users involved in the test are configured with the **mysql_native_password** plugin. 74 | 5. The `./configure --with-resp-payload` option for `intercept` cannot be used together with the `./configure` option for `tcpcopy`. 75 | 6. **tcpcopy with mysql-replay-module** only supports the MySQL protocol. 76 | 7. For additional assistance, visit [tcpcopy](https://github.com/session-replay-tools/tcpcopy). 77 | 78 | ## Release History 79 | 80 | + 2017.03 v1.0 mysql-replay-module released 81 | + 2024.09 v1.0 Open source fully uses English 82 | 83 | ## Bugs and Feature Requests 84 | 85 | Have a bug or a feature request? [Please open a new issue](https://github.com/session-replay-tools/mysql-replay-module/issues). Before opening any issue, please search for existing issues. 86 | 87 | ## Copyright and License 88 | 89 | Copyright 2024 under [the BSD license](LICENSE). 90 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | tc_addon_name=tc_mysql_module 2 | PROTOCOL_MODULES="tc_mysql_module" 3 | TC_PAYLOAD=YES 4 | TC_DIGEST=YES 5 | mysql_header="$tc_addon_dir/password.h $tc_addon_dir/pairs.h $tc_addon_dir/protocol.h" 6 | mysql_src="$tc_addon_dir/password.c $tc_addon_dir/pairs.c $tc_addon_dir/protocol.c" 7 | TC_ADDON_DEPS="$TC_ADDON_DEPS $mysql_header" 8 | TC_ADDON_SRCS="$mysql_src $tc_addon_dir/tc_mysql_module.c" 9 | -------------------------------------------------------------------------------- /pairs.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "pairs.h" 4 | 5 | static hash_table *user_pwd_table = NULL; 6 | 7 | static uint64_t 8 | get_key_from_user(char *user) 9 | { 10 | size_t len, i; 11 | uint64_t key = 0; 12 | 13 | if (user == NULL) { 14 | return key; 15 | } 16 | 17 | len = strlen(user); 18 | for (i = 0; i < len; i++ ) { 19 | key = 31*key + user[i]; 20 | } 21 | 22 | return key; 23 | } 24 | 25 | char * 26 | retrieve_user_pwd(char *user) 27 | { 28 | uint64_t key; 29 | mysql_user *p_user_info; 30 | 31 | if (user_pwd_table == NULL) { 32 | tc_log_info(LOG_ERR, 0, "empty user info in conf/plugin.conf"); 33 | fprintf(stderr, "empty user info in conf/plugin.conf\n"); 34 | return NULL; 35 | } 36 | 37 | key = get_key_from_user(user); 38 | p_user_info = hash_find(user_pwd_table, key); 39 | 40 | while (p_user_info) { 41 | if (strcmp(p_user_info->user, user) == 0) { 42 | return p_user_info->password; 43 | } 44 | p_user_info = p_user_info->next; 45 | } 46 | 47 | return NULL; 48 | } 49 | 50 | int 51 | retrieve_mysql_user_pwd_info(tc_pool_t *pool, char *pairs) 52 | { 53 | char *p, *end, *q, *next, *pair_end; 54 | size_t len; 55 | uint64_t key; 56 | mysql_user *p_user_info, *p_tmp_user_info; 57 | 58 | user_pwd_table = hash_create(pool, 256); 59 | user_pwd_table->pool = pool; 60 | 61 | p = pairs; 62 | len = strlen(p); 63 | end = p + len; 64 | 65 | if (len <= 1) { 66 | tc_log_info(LOG_WARN, 0, "use password error:%s:", pairs); 67 | return -1; 68 | } 69 | 70 | do { 71 | next = strchr(p, ','); 72 | q = strchr(p, '@'); 73 | 74 | if (next != NULL) { 75 | if (next != p) { 76 | pair_end = next - 1; 77 | } else { 78 | tc_log_info(LOG_WARN, 0, "use password error:%s:", pairs); 79 | return -1; 80 | } 81 | } else { 82 | pair_end = p + strlen(p) - 1; 83 | } 84 | 85 | if (q == NULL || pair_end <= q) { 86 | tc_log_info(LOG_WARN, 0, "user without password is found"); 87 | return -1; 88 | } 89 | 90 | if ((q - p) >= 256 || (pair_end - q) >= 256) { 91 | tc_log_info(LOG_WARN, 0, "too long for user or password"); 92 | return -1; 93 | } 94 | 95 | p_user_info = (mysql_user *) tc_pcalloc(pool, sizeof(mysql_user)); 96 | if (p_user_info == NULL) { 97 | return -1; 98 | } 99 | 100 | strncpy(p_user_info->user, p, q - p); 101 | strncpy(p_user_info->password, q + 1, pair_end - q); 102 | key = get_key_from_user(p_user_info->user); 103 | p_tmp_user_info = hash_find(user_pwd_table, key); 104 | 105 | if (p_tmp_user_info == NULL) { 106 | hash_add(user_pwd_table, pool, key, (void *) p_user_info); 107 | } else { 108 | p_tmp_user_info->next = p_user_info; 109 | } 110 | 111 | if (next != NULL) { 112 | p = next + 1; 113 | } else { 114 | break; 115 | } 116 | } while (p < end); 117 | 118 | return 0; 119 | } 120 | 121 | -------------------------------------------------------------------------------- /pairs.h: -------------------------------------------------------------------------------- 1 | #ifndef PAIRS_INCLUDED 2 | #define PAIRS_INCLUDED 3 | #include 4 | #define MAX_PASSWORD_LEN 256 5 | #define MAX_USER_LEN 256 6 | 7 | typedef struct mysql_user{ 8 | char user[MAX_USER_LEN]; 9 | char password[MAX_PASSWORD_LEN]; 10 | struct mysql_user* next; 11 | }mysql_user; 12 | 13 | char *retrieve_user_pwd(char *user); 14 | int retrieve_mysql_user_pwd_info(tc_pool_t *, char *); 15 | 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /password.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "password.h" 4 | 5 | static void 6 | my_crypt(char *to, const unsigned char *s1, const unsigned char *s2, uint len) 7 | { 8 | const unsigned char *s1_end = s1 + len; 9 | 10 | while (s1 < s1_end) { 11 | *to++= *s1++ ^ *s2++; 12 | } 13 | } 14 | 15 | /** 16 | * SHA1(password) XOR 17 | * SHA1("20-bytes random data from server" SHA1(SHA1(password))) 18 | */ 19 | void 20 | scramble(char *to, const char *message, const char *password) 21 | { 22 | unsigned char hash_stage1[SHA1_HASH_SIZE]; 23 | unsigned char hash_stage2[SHA1_HASH_SIZE]; 24 | 25 | tc_sha1_digest_one(hash_stage1, SHA1_HASH_SIZE, 26 | (const unsigned char *) password, strlen(password)); 27 | tc_sha1_digest_one(hash_stage2, SHA1_HASH_SIZE, 28 | hash_stage1, SHA1_HASH_SIZE); 29 | tc_sha1_digest_two((unsigned char *) to, SCRAMBLE_LENGTH, 30 | (const unsigned char *) message, SCRAMBLE_LENGTH, 31 | (const unsigned char *) hash_stage2, SHA1_HASH_SIZE); 32 | 33 | my_crypt(to, (const unsigned char *) to, hash_stage1, SCRAMBLE_LENGTH); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /password.h: -------------------------------------------------------------------------------- 1 | #ifndef PASSWORD_INCLUDED 2 | #define PASSWORD_INCLUDED 3 | 4 | #define SCRAMBLE_LENGTH 20 5 | #define SHA1_HASH_SIZE 20 6 | 7 | void scramble(char *to, const char *message, const char *password); 8 | 9 | #endif /* ----- #ifndef PASSWORD_INCLUDED ----- */ 10 | 11 | -------------------------------------------------------------------------------- /protocol.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pairs.h" 3 | #include "protocol.h" 4 | #include "password.h" 5 | 6 | static void 7 | new_hash(uint64_t *result, const char *password) 8 | { 9 | int i = 0, length; 10 | uint64_t nr = 1345345333L, add = 7, nr2 = 0x12345671L, tmp; 11 | 12 | length = strlen(password); 13 | 14 | for (; i < length; ++i) { 15 | if (' ' == password[i] || '\t' == password[i]) { 16 | /* skip spaces */ 17 | continue; 18 | } 19 | 20 | tmp = (0xff & password[i]); 21 | nr ^= ((((nr & 63) + add) * tmp) + (nr << 8)); 22 | nr2 += ((nr2 << 8) ^ nr); 23 | add += tmp; 24 | } 25 | 26 | result[0] = nr & 0x7fffffffL; 27 | result[1] = nr2 & 0x7fffffffL; 28 | 29 | } 30 | 31 | /* 32 | * right from Monty's code 33 | */ 34 | void 35 | new_crypt(char *result, const char *password, char *message) 36 | { 37 | int length, i; 38 | char b; 39 | double d; 40 | uint64_t pw[2], msg[2], max, seed1, seed2; 41 | 42 | new_hash(pw, message); 43 | new_hash(msg, password); 44 | 45 | max = 0x3fffffffL; 46 | seed1 = (pw[0] ^ msg[0]) % max; 47 | seed2 = (pw[1] ^ msg[1]) % max; 48 | length = strlen(message); 49 | 50 | for (i =0; i < length; i++) { 51 | seed1 = ((seed1 * 3) + seed2) % max; 52 | seed2 = (seed1 + seed2 + 33) % max; 53 | d = (double) seed1 / (double) max; 54 | b = (char) floor ((d * 31) + 64); 55 | result[i] = b; 56 | } 57 | 58 | seed1 = ((seed1 * 3) + seed2) % max; 59 | 60 | d = (double) seed1 / (double) max; 61 | b = (char) floor (d * 31); 62 | 63 | for (i = 0; i < length; i++) { 64 | result[i] ^= (char) b; 65 | } 66 | } 67 | 68 | int 69 | is_last_data_packet(unsigned char *payload) 70 | { 71 | size_t len; 72 | unsigned char *p; 73 | 74 | p = payload; 75 | len = p[0] + (p[1] << 8) + (p[2] << 16); 76 | 77 | if (len < 9) { 78 | /* skip packet length */ 79 | p = p + 3; 80 | /* skip packet number */ 81 | p = p + 1; 82 | if (254 == p[0]) { 83 | return 1; 84 | } 85 | } 86 | return 0; 87 | } 88 | 89 | int 90 | parse_handshake_init_cont(unsigned char *payload, size_t length, 91 | char *scramble_buff) 92 | { 93 | /* 94 | * The following is the protocol format of mysql handshake 95 | * 96 | * 1 protocol_version 97 | * n (Null-Terminated String) server_version 98 | * 4 thread_id 99 | * 8 scramble_buff 100 | * 1 (filler) always 0x00 101 | * 2 server_capabilities 102 | * 1 server_language 103 | * 2 server_status 104 | * 2 server capabilities (two upper bytes) 105 | * 1 length of the scramble 106 | * 10 (filler) always 0 107 | * n rest of the plugin provided data 108 | * (at least 12 bytes) 109 | * 1 \0 byte, terminating 110 | * the second part of a scramble 111 | */ 112 | char *str; 113 | size_t len, count, str_len; 114 | unsigned char *p; 115 | 116 | p = payload; 117 | /* skip packet length */ 118 | p = p + 3; 119 | /* skip packet number */ 120 | p = p + 1; 121 | /* skip protocol_version */ 122 | p++; 123 | str = (char *) p; 124 | len = strlen(str); 125 | /* skip server_version */ 126 | p = p + len + 1; 127 | /* skip thread_id */ 128 | p += 4; 129 | count = p - payload + 8; 130 | if (count > length) { 131 | tc_log_info(LOG_ERR, 0, "payload len is too short for init:%u,%u", 132 | length, count); 133 | return 0; 134 | } 135 | strncpy(scramble_buff, (char *) p, 8); 136 | /* skip scramble_buff */ 137 | p = p + 8 + 1; 138 | /* skip server_capabilities */ 139 | p = p + 2; 140 | /* skip server_language */ 141 | p = p + 1; 142 | /* skip server_status */ 143 | p = p + 2; 144 | /* skip server capabilities (two upper bytes) */ 145 | p = p + 2; 146 | /* skip length of the scramble */ 147 | p = p + 1; 148 | /* skip (filler) always 0 */ 149 | p = p + 10; 150 | str = (char *) p; 151 | str_len = strlen(str) + 8; 152 | count = p - payload + strlen(str); 153 | if (str_len > SCRAMBLE_LENGTH|| count > length) { 154 | if (count >length) { 155 | tc_log_info(LOG_ERR, 0, "payload len is too short for init2:%u,%u", 156 | length, count); 157 | } else { 158 | tc_log_info(LOG_ERR, 0, "scramble is too long:%u", str_len); 159 | } 160 | return 0; 161 | } 162 | /* copy the rest of scramble_buff */ 163 | strncpy(scramble_buff + 8, str, strlen(str)); 164 | 165 | return 1; 166 | } 167 | 168 | int 169 | change_clt_auth_content(unsigned char *payload, int length, 170 | char *password, char *message) 171 | { 172 | /* 173 | * 4 client_flags 174 | * 4 max_packet_size 175 | * 1 charset_number 176 | * 23 (filler) always 0x00... 177 | * n (Null-Terminated String) user 178 | * n (Length Coded Binary) scramble_buff (1 + x bytes) 179 | * n (Null-Terminated String) databasename (optional) 180 | */ 181 | char *str, user[256], *pwd; 182 | size_t len, i; 183 | unsigned char *p, *q, scramble_buff[SCRAMBLE_LENGTH + 1]; 184 | 185 | tc_memzero(scramble_buff, SCRAMBLE_LENGTH + 1); 186 | 187 | p = payload; 188 | /* skip mysql packet header */ 189 | /* skip packet length */ 190 | p = p + 3; 191 | /* skip packet number */ 192 | p = p + 1; 193 | /* skip client_flags */ 194 | p = p + 4; 195 | /* skip max_packet_size */ 196 | p = p + 4; 197 | /* skip charset_number */ 198 | p = p + 1; 199 | /* skip (filler) always 0x00... */ 200 | q = p; 201 | p = p + 23; 202 | len = p - payload; 203 | if ((int) len > length) { 204 | tc_log_info(LOG_ERR, 0, "payload len is too short:%d,%u", length, len); 205 | return 0; 206 | } 207 | 208 | for (i = 0; i < 23; i++) { 209 | if (q[i] != 0 ) { 210 | tc_log_info(LOG_WARN, 0, "it is not a login packet"); 211 | return 0; 212 | } 213 | } 214 | 215 | str = (char *) p; 216 | /* retrieve user */ 217 | tc_memzero(user, 256); 218 | 219 | len = strlen(str); 220 | if (len >= 256) { 221 | tc_log_info(LOG_ERR, 0, "user len is too long:%s,%u", str, len); 222 | return 0; 223 | } 224 | strcpy(user, str); 225 | 226 | pwd = retrieve_user_pwd(user); 227 | if (pwd == NULL) { 228 | tc_log_info(LOG_WARN, 0, "user:%s,pwd is null", user); 229 | return 0; 230 | } 231 | 232 | /* skip user */ 233 | p = p + len + 1; 234 | 235 | /* skip scramble_buff length */ 236 | p = p + 1; 237 | len = p - payload + SCRAMBLE_LENGTH; 238 | if ((int) len > length) { 239 | tc_log_info(LOG_ERR, 0, "payload len is too short too:%d,%u", 240 | length, len); 241 | return 0; 242 | } 243 | 244 | scramble((char *) scramble_buff, message, pwd); 245 | 246 | /* change scramble_buff according the target server scramble */ 247 | for (i = 0; i < SCRAMBLE_LENGTH; i++) { 248 | p[i] = scramble_buff[i]; 249 | } 250 | 251 | /* save password */ 252 | strcpy(password, pwd); 253 | 254 | return 1; 255 | } 256 | 257 | int 258 | change_clt_second_auth_content(unsigned char *payload, size_t length, 259 | char *new_content) 260 | { 261 | size_t i, len; 262 | unsigned char *p; 263 | 264 | p = payload; 265 | /* skip mysql packet header */ 266 | /* skip packet length */ 267 | p = p + 3; 268 | 269 | /* skip packet number */ 270 | p = p + 1; 271 | 272 | len = p - payload + 8; 273 | if (len > length) { 274 | tc_log_info(LOG_ERR, 0, "payload len is too short for sec :%u,%u", 275 | length, len); 276 | return 0; 277 | } 278 | 279 | /* change scramble_buff according to the target server scramble */ 280 | for (i = 0; i < 8; i++) { 281 | p[i] = new_content[i]; 282 | } 283 | return 1; 284 | 285 | } 286 | 287 | -------------------------------------------------------------------------------- /protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOCOL_INCLUDED 2 | #define PROTOCOL_INCLUDED 3 | #include 4 | /* 5 | * We support only mysql 4.1 and later. 6 | * SSL is not supported here 7 | */ 8 | 9 | int is_last_data_packet(unsigned char *payload); 10 | void new_crypt(char *result, const char *password, char *message); 11 | int parse_handshake_init_cont(unsigned char *payload, 12 | size_t length, char *scramble); 13 | int change_clt_auth_content(unsigned char *payload, 14 | int length, char *password, char *message); 15 | int change_clt_second_auth_content(unsigned char *payload, 16 | size_t length, char *new_content); 17 | 18 | #endif /* ----- #ifndef PROTOCOL_INCLUDED ----- */ 19 | 20 | -------------------------------------------------------------------------------- /tc_mysql_module.c: -------------------------------------------------------------------------------- 1 | #include "password.h" 2 | #include "pairs.h" 3 | #include "protocol.h" 4 | #include 5 | #include 6 | 7 | #define COM_STMT_PREPARE 22 8 | #define COM_STMT_EXECUTE 23 9 | #define COM_QUERY 3 10 | #define MAX_SP_SIZE 256 11 | #define MAX_USER_INFO 4096 12 | #define ENCRYPT_LEN 16 13 | #define SEED_323_LENGTH 8 14 | 15 | typedef struct { 16 | time_t last_refresh_time; 17 | uint32_t seq_after_ps; 18 | uint32_t sec_auth_checked:1; 19 | uint32_t sec_auth_not_yet_done:1; 20 | uint32_t first_auth_sent:1; 21 | uint32_t auth_packet_already_added:1; 22 | uint32_t update_auth_table_item_switch:4; 23 | char scramble[SCRAMBLE_LENGTH + 1]; 24 | char seed323[SEED_323_LENGTH + 1]; 25 | char password[MAX_PASSWORD_LEN]; 26 | } tc_mysql_session; 27 | 28 | 29 | typedef struct { 30 | link_list *list; 31 | int tot_cont_len; 32 | } mysql_table_item_t; 33 | 34 | 35 | typedef struct { 36 | tc_pool_t *fir_auth_pool; 37 | tc_pool_t *sec_auth_pool; 38 | tc_pool_t *ps_pool; 39 | hash_table *fir_auth_table; 40 | hash_table *sec_auth_table; 41 | hash_table *ps_table; 42 | } tc_mysql_ctx_t; 43 | 44 | /* TODO allocate it on heap */ 45 | static tc_mysql_ctx_t ctx; 46 | 47 | 48 | static int 49 | init_mysql_module() 50 | { 51 | tc_pool_t *pool; 52 | 53 | pool = tc_create_pool(TC_PLUGIN_POOL_SIZE, TC_PLUGIN_POOL_SUB_SIZE, 0); 54 | if (pool) { 55 | #if (TC_DETECT_MEMORY) 56 | pool->d.is_traced = 1; 57 | #endif 58 | ctx.fir_auth_pool = pool; 59 | } else { 60 | return TC_ERR; 61 | } 62 | 63 | pool = tc_create_pool(TC_PLUGIN_POOL_SIZE, TC_PLUGIN_POOL_SUB_SIZE, 0); 64 | if (pool) { 65 | ctx.sec_auth_pool = pool; 66 | } else { 67 | return TC_ERR; 68 | } 69 | 70 | pool = tc_create_pool(TC_PLUGIN_POOL_SIZE, TC_PLUGIN_POOL_SUB_SIZE, 0); 71 | if (pool) { 72 | ctx.ps_pool = pool; 73 | } else { 74 | return TC_ERR; 75 | } 76 | 77 | ctx.fir_auth_table = hash_create(ctx.fir_auth_pool, 65536); 78 | if (ctx.fir_auth_table == NULL) { 79 | return TC_ERR; 80 | } 81 | 82 | ctx.sec_auth_table = hash_create(ctx.sec_auth_pool, 65536); 83 | if (ctx.sec_auth_table == NULL) { 84 | return TC_ERR; 85 | } 86 | 87 | ctx.ps_table = hash_create(ctx.ps_pool, 65536); 88 | if (ctx.ps_table == NULL) { 89 | return TC_ERR; 90 | } 91 | 92 | return TC_OK; 93 | } 94 | 95 | static unsigned char * 96 | copy_packet(tc_pool_t *pool, void *data) 97 | { 98 | int frame_len; 99 | uint16_t tot_len; 100 | unsigned char *frame; 101 | 102 | tc_iph_t *ip = (tc_iph_t *) (((unsigned char *) data) + ETHERNET_HDR_LEN); 103 | tot_len = ntohs(ip->tot_len); 104 | frame_len = ETHERNET_HDR_LEN + tot_len; 105 | 106 | frame = (unsigned char *) tc_palloc(pool, frame_len); 107 | 108 | if (frame != NULL) { 109 | memcpy(frame + ETHERNET_HDR_LEN, ip, tot_len); 110 | } 111 | 112 | return frame; 113 | } 114 | 115 | 116 | static int 117 | remove_or_refresh_fir_auth(uint64_t key, int is_refresh) 118 | { 119 | void *value, *new_value; 120 | value = hash_find(ctx.fir_auth_table, key); 121 | 122 | if (value != NULL) { 123 | hash_del(ctx.fir_auth_table, ctx.fir_auth_pool, key); 124 | if (is_refresh) { 125 | new_value = copy_packet(ctx.fir_auth_pool, value); 126 | hash_add(ctx.fir_auth_table, ctx.fir_auth_pool, key, new_value); 127 | #if (TC_DETECT_MEMORY) 128 | tc_log_info(LOG_INFO, 0, "refresh fir auth:%llu, new addr:%p", 129 | key, new_value); 130 | #endif 131 | } 132 | #if (TC_DETECT_MEMORY) 133 | tc_log_info(LOG_INFO, 0, "free value:%p for key:%llu", value, key); 134 | #endif 135 | tc_pfree(ctx.fir_auth_pool, value); 136 | } 137 | 138 | return TC_OK; 139 | } 140 | 141 | 142 | static int 143 | remove_or_refresh_sec_auth(uint64_t key, int is_refresh) 144 | { 145 | void *value, *new_value; 146 | 147 | value = hash_find(ctx.sec_auth_table, key); 148 | if (value != NULL) { 149 | hash_del(ctx.sec_auth_table, ctx.sec_auth_pool, key); 150 | if (is_refresh) { 151 | new_value = copy_packet(ctx.sec_auth_pool, value); 152 | hash_add(ctx.sec_auth_table, ctx.sec_auth_pool, key, new_value); 153 | #if (TC_DETECT_MEMORY) 154 | tc_log_info(LOG_INFO, 0, "refresh sec auth:%llu, new addr:%p", 155 | key, new_value); 156 | #endif 157 | } 158 | #if (TC_DETECT_MEMORY) 159 | tc_log_info(LOG_INFO, 0, "free value:%p for key:%llu", value, key); 160 | #endif 161 | tc_pfree(ctx.sec_auth_pool, value); 162 | } 163 | 164 | return TC_OK; 165 | } 166 | 167 | 168 | static int 169 | remove_or_refresh_ps_stmt(uint64_t key, int is_refresh) 170 | { 171 | void *value, *pkt; 172 | link_list *list; 173 | p_link_node ln, tln, new_ln; 174 | mysql_table_item_t *item, *new_item; 175 | 176 | value = hash_find(ctx.ps_table, key); 177 | if (value != NULL) { 178 | 179 | item = value; 180 | 181 | if (is_refresh) { 182 | new_item = tc_pcalloc(ctx.ps_pool, sizeof(mysql_table_item_t)); 183 | if (new_item != NULL) { 184 | new_item->list = link_list_create(ctx.ps_pool); 185 | if (new_item->list == NULL) { 186 | tc_log_info(LOG_ERR, 0, "list create err"); 187 | is_refresh = 0; 188 | } else { 189 | new_item->tot_cont_len = item->tot_cont_len; 190 | } 191 | } else { 192 | tc_log_info(LOG_ERR, 0, "mysql item create err"); 193 | is_refresh = 0; 194 | } 195 | } 196 | 197 | list = item->list; 198 | ln = link_list_first(list); 199 | while (ln) { 200 | tln = ln; 201 | ln = link_list_get_next(list, ln); 202 | link_list_remove(list, tln); 203 | if (is_refresh) { 204 | pkt = (unsigned char *) copy_packet(ctx.ps_pool, tln->data); 205 | new_ln = link_node_malloc(ctx.ps_pool, pkt); 206 | new_ln->key = tln->key; 207 | link_list_append_by_order(new_item->list, new_ln); 208 | #if (TC_DETECT_MEMORY) 209 | tc_log_info(LOG_INFO, 0, "refresh ps:%llu, new addr:%p, ln:%p", 210 | key, pkt, new_ln); 211 | #endif 212 | } 213 | tc_pfree(ctx.ps_pool, tln->data); 214 | tc_pfree(ctx.ps_pool, tln); 215 | } 216 | 217 | tc_pfree(ctx.ps_pool, value); 218 | tc_pfree(ctx.ps_pool, list); 219 | 220 | hash_del(ctx.ps_table, ctx.ps_pool, key); 221 | 222 | if (is_refresh) { 223 | hash_add(ctx.ps_table, ctx.ps_pool, key, new_item); 224 | #if (TC_DETECT_MEMORY) 225 | tc_log_info(LOG_INFO, 0, "refresh:%llu, new item:%p", 226 | key, new_item); 227 | #endif 228 | } 229 | } 230 | 231 | return TC_OK; 232 | } 233 | 234 | 235 | static int 236 | release_resources(uint64_t key) 237 | { 238 | remove_or_refresh_fir_auth(key, 0); 239 | remove_or_refresh_sec_auth(key, 0); 240 | remove_or_refresh_ps_stmt(key, 0); 241 | 242 | return TC_OK; 243 | } 244 | 245 | static int 246 | refresh_resources(uint64_t key) 247 | { 248 | remove_or_refresh_fir_auth(key, 1); 249 | remove_or_refresh_sec_auth(key, 1); 250 | remove_or_refresh_ps_stmt(key, 1); 251 | 252 | return TC_OK; 253 | } 254 | 255 | static void 256 | remove_table_obsolete_items(time_t thresh_access_tme) 257 | { 258 | uint32_t i, cnt = 0; 259 | link_list *l; 260 | hash_node *hn; 261 | p_link_node ln, next_ln; 262 | 263 | if (ctx.fir_auth_table == NULL || ctx.fir_auth_table->total == 0) { 264 | return; 265 | } 266 | 267 | for (i = 0; i < ctx.fir_auth_table->size; i ++) { 268 | l = get_link_list(ctx.fir_auth_table, i); 269 | if (l->size > 0) { 270 | ln = link_list_first(l); 271 | while (ln) { 272 | hn = (hash_node *) ln->data; 273 | next_ln = link_list_get_next(l, ln); 274 | if (hn->access_time < thresh_access_tme) { 275 | tc_log_info(LOG_INFO, 0, 276 | "key:%llu, access time:%u, thresh_access_tme:%u", 277 | hn->key, hn->access_time, thresh_access_tme); 278 | 279 | release_resources(hn->key); 280 | } 281 | ln = next_ln; 282 | } 283 | 284 | cnt += l->size; 285 | 286 | if (ctx.fir_auth_table->total == cnt) { 287 | break; 288 | } 289 | } 290 | } 291 | } 292 | 293 | 294 | static void 295 | remove_obsolete_resources(int is_full) 296 | { 297 | time_t thresh_access_tme; 298 | 299 | if (is_full) { 300 | thresh_access_tme = tc_time() + 1; 301 | } else { 302 | thresh_access_tme = tc_time() - MAX_IDLE_TIME; 303 | } 304 | 305 | remove_table_obsolete_items(thresh_access_tme); 306 | } 307 | 308 | 309 | static void 310 | exit_mysql_module() 311 | { 312 | tc_log_info(LOG_INFO, 0, "call exit_mysql_module"); 313 | 314 | remove_obsolete_resources(1); 315 | 316 | if (ctx.fir_auth_pool != NULL) { 317 | tc_destroy_pool(ctx.fir_auth_pool); 318 | ctx.fir_auth_pool = NULL; 319 | ctx.fir_auth_table = NULL; 320 | } 321 | 322 | if (ctx.sec_auth_pool != NULL) { 323 | tc_destroy_pool(ctx.sec_auth_pool); 324 | ctx.sec_auth_pool = NULL; 325 | ctx.sec_auth_table = NULL; 326 | } 327 | 328 | if (ctx.ps_pool != NULL) { 329 | tc_destroy_pool(ctx.ps_pool); 330 | ctx.ps_pool = NULL; 331 | ctx.ps_table = NULL; 332 | } 333 | } 334 | 335 | 336 | static bool 337 | check_renew_session(tc_iph_t *ip, tc_tcph_t *tcp) 338 | { 339 | void *value; 340 | uint16_t size_ip, size_tcp, tot_len, cont_len; 341 | uint64_t key; 342 | unsigned char *payload, command, pack_number; 343 | 344 | if (ctx.fir_auth_table == NULL) { 345 | return false; 346 | } 347 | 348 | key = get_key(ip->saddr, tcp->source); 349 | value = hash_find(ctx.fir_auth_table, key); 350 | if (value == NULL) { 351 | return false; 352 | } 353 | 354 | size_ip = ip->ihl << 2; 355 | size_tcp = tcp->doff << 2; 356 | tot_len = ntohs(ip->tot_len); 357 | cont_len = tot_len - size_tcp - size_ip; 358 | 359 | if (cont_len > 0) { 360 | payload = (unsigned char *) ((char *) tcp + size_tcp); 361 | /* skip packet length */ 362 | payload = payload + 3; 363 | /* retrieve packet number */ 364 | pack_number = payload[0]; 365 | /* if it is the second authenticate_user, skip it */ 366 | if (pack_number != 0) { 367 | return false; 368 | } 369 | /* skip packet number */ 370 | payload = payload + 1; 371 | 372 | command = payload[0]; 373 | tc_log_debug1(LOG_DEBUG, 0, "mysql command:%u", command); 374 | if (command == COM_QUERY || command == COM_STMT_EXECUTE) { 375 | return true; 376 | } 377 | } 378 | 379 | return false; 380 | } 381 | 382 | 383 | static bool 384 | check_pack_needed_for_recons(tc_sess_t *s, tc_iph_t *ip, tc_tcph_t *tcp) 385 | { 386 | int diff; 387 | uint16_t size_tcp; 388 | p_link_node ln; 389 | unsigned char *payload, command, *pkt; 390 | tc_mysql_session *mysql_sess; 391 | mysql_table_item_t *item; 392 | 393 | mysql_sess = s->data; 394 | 395 | if (s->sm.fake_syn) { 396 | if (before(ntohl(tcp->seq), mysql_sess->seq_after_ps)) { 397 | return false; 398 | } 399 | } 400 | 401 | if (s->cur_pack.cont_len > 0) { 402 | 403 | size_tcp = tcp->doff << 2; 404 | payload = (unsigned char *) ((char *) tcp + size_tcp); 405 | /* skip packet length */ 406 | payload = payload + 3; 407 | /* skip packet number */ 408 | payload = payload + 1; 409 | command = payload[0]; 410 | 411 | if (command != COM_STMT_PREPARE) { 412 | 413 | diff = tc_time() - mysql_sess->last_refresh_time; 414 | 415 | if (diff >= MAX_RETHRESH_TIME) { 416 | refresh_resources(s->hash_key); 417 | mysql_sess->last_refresh_time = tc_time(); 418 | mysql_sess->update_auth_table_item_switch = 0; 419 | #if (TC_DETECT_MEMORY) 420 | tc_log_info(LOG_NOTICE, 0, "refresh time:%u for key:%llu", 421 | mysql_sess->last_refresh_time, s->hash_key); 422 | #endif 423 | } 424 | 425 | mysql_sess->update_auth_table_item_switch++; 426 | if (mysql_sess->update_auth_table_item_switch == 0) { 427 | if (hash_find(ctx.fir_auth_table, s->hash_key) == NULL) { 428 | tc_log_info(LOG_NOTICE, 0, "no fir auth for key:%llu", 429 | s->hash_key); 430 | } 431 | } 432 | 433 | return false; 434 | } 435 | 436 | item = hash_find(ctx.ps_table, s->hash_key); 437 | 438 | if (!item) { 439 | item = tc_pcalloc(ctx.ps_pool, sizeof(mysql_table_item_t)); 440 | if (item != NULL) { 441 | item->list = link_list_create(ctx.ps_pool); 442 | if (item->list != NULL) { 443 | hash_add(ctx.ps_table, ctx.ps_pool, s->hash_key, item); 444 | } else { 445 | tc_log_info(LOG_ERR, 0, "list create err"); 446 | return false; 447 | } 448 | } else { 449 | tc_log_info(LOG_ERR, 0, "mysql item create err"); 450 | return false; 451 | } 452 | } 453 | 454 | if (item->list->size > MAX_SP_SIZE) { 455 | tc_log_info(LOG_INFO, 0, "too many prepared stmts for a session"); 456 | return false; 457 | } 458 | 459 | tc_log_debug1(LOG_INFO, 0, "push packet:%u", ntohs(s->src_port)); 460 | 461 | pkt = (unsigned char *) cp_fr_ip_pack(ctx.ps_pool, ip); 462 | ln = link_node_malloc(ctx.ps_pool, pkt); 463 | ln->key = ntohl(tcp->seq); 464 | link_list_append_by_order(item->list, ln); 465 | item->tot_cont_len += s->cur_pack.cont_len; 466 | 467 | return true; 468 | } 469 | 470 | return false; 471 | } 472 | 473 | 474 | static int 475 | mysql_dispose_auth(tc_sess_t *s, tc_iph_t *ip, tc_tcph_t *tcp) 476 | { 477 | int auth_success; 478 | void *value; 479 | char encryption[ENCRYPT_LEN]; 480 | uint16_t size_tcp, cont_len; 481 | unsigned char *payload; 482 | tc_mysql_session *mysql_sess; 483 | 484 | mysql_sess = s->data; 485 | 486 | size_tcp = tcp->doff << 2; 487 | cont_len = s->cur_pack.cont_len; 488 | 489 | if (!mysql_sess->first_auth_sent) { 490 | 491 | payload = (unsigned char *) ((char *) tcp + size_tcp); 492 | tc_log_debug1(LOG_INFO, 0, "change fir auth:%u", ntohs(s->src_port)); 493 | auth_success = change_clt_auth_content(payload, (int) cont_len, 494 | mysql_sess->password, mysql_sess->scramble); 495 | 496 | if (!auth_success) { 497 | s->sm.sess_over = 1; 498 | tc_log_info(LOG_WARN, 0, "change fir auth unsuccessful"); 499 | return TC_ERR; 500 | } 501 | 502 | mysql_sess->first_auth_sent = 1; 503 | 504 | if (!s->sm.fake_syn) { 505 | release_resources(s->hash_key); 506 | 507 | value = (void *) cp_fr_ip_pack(ctx.fir_auth_pool, ip); 508 | hash_add(ctx.fir_auth_table, ctx.fir_auth_pool, s->hash_key, value); 509 | mysql_sess->last_refresh_time = tc_time(); 510 | 511 | #if (TC_DETECT_MEMORY) 512 | tc_log_info(LOG_INFO, 0, "s:%p,hash add fir auth:%llu,value:%p, p:%u", 513 | s, s->hash_key, value, ntohs(s->src_port)); 514 | #endif 515 | } else { 516 | hash_node *hn = hash_find_node(ctx.fir_auth_table, s->hash_key); 517 | if (hn != NULL) { 518 | mysql_sess->last_refresh_time = hn->create_time; 519 | } else { 520 | tc_log_info(LOG_WARN, 0, "hash node for key:%llu is nil", 521 | s->hash_key); 522 | } 523 | } 524 | 525 | } else if (mysql_sess->first_auth_sent && mysql_sess->sec_auth_not_yet_done) 526 | { 527 | payload = (unsigned char *) ((char *) tcp + size_tcp); 528 | 529 | tc_memzero(encryption, ENCRYPT_LEN); 530 | tc_memzero(mysql_sess->seed323, SEED_323_LENGTH + 1); 531 | memcpy(mysql_sess->seed323, mysql_sess->scramble, SEED_323_LENGTH); 532 | new_crypt(encryption, mysql_sess->password, mysql_sess->seed323); 533 | 534 | tc_log_debug1(LOG_INFO, 0, "change sec auth:%u", ntohs(s->src_port)); 535 | change_clt_second_auth_content(payload, cont_len, encryption); 536 | mysql_sess->sec_auth_not_yet_done = 0; 537 | 538 | if (!s->sm.fake_syn) { 539 | value = (void *) cp_fr_ip_pack(ctx.sec_auth_pool, ip); 540 | hash_add(ctx.sec_auth_table, ctx.sec_auth_pool, s->hash_key, value); 541 | } 542 | } 543 | 544 | return TC_OK; 545 | } 546 | 547 | 548 | static int 549 | prepare_for_renew_session(tc_sess_t *s, tc_iph_t *ip, tc_tcph_t *tcp) 550 | { 551 | uint16_t size_ip, fir_clen, sec_clen; 552 | uint32_t tot_clen, base_seq; 553 | uint64_t key; 554 | tc_iph_t *fir_ip, *t_ip, *sec_ip; 555 | tc_tcph_t *fir_tcp, *t_tcp, *sec_tcp; 556 | p_link_node ln; 557 | unsigned char *p; 558 | mysql_table_item_t *item; 559 | tc_mysql_session *mysql_sess; 560 | 561 | mysql_sess = s->data; 562 | if (mysql_sess == NULL) { 563 | tc_log_info(LOG_WARN, 0, "mysql session structure is not allocated"); 564 | return TC_ERR; 565 | } else if (mysql_sess->auth_packet_already_added) { 566 | tc_log_info(LOG_NOTICE, 0, "dup visit prepare_for_renew_session"); 567 | return TC_OK; 568 | } 569 | 570 | sec_ip = NULL; 571 | sec_tcp = NULL; 572 | s->sm.need_rep_greet = 1; 573 | 574 | key = s->hash_key; 575 | 576 | p = (unsigned char *) hash_find(ctx.fir_auth_table, key); 577 | 578 | if (p != NULL) { 579 | fir_ip = (tc_iph_t *) (p + ETHERNET_HDR_LEN); 580 | size_ip = fir_ip->ihl << 2; 581 | fir_tcp = (tc_tcph_t *) ((char *) fir_ip + size_ip); 582 | fir_clen = TCP_PAYLOAD_LENGTH(fir_ip, fir_tcp); 583 | tot_clen = fir_clen; 584 | } else { 585 | tc_log_info(LOG_WARN, 0, "no first auth:%u", ntohs(s->src_port)); 586 | return TC_ERR; 587 | } 588 | 589 | p = (unsigned char *) hash_find(ctx.sec_auth_table, key); 590 | if (p != NULL) { 591 | sec_ip = (tc_iph_t *) (p + ETHERNET_HDR_LEN); 592 | size_ip = sec_ip->ihl << 2; 593 | sec_tcp = (tc_tcph_t *) ((char *) sec_ip + size_ip); 594 | sec_clen = TCP_PAYLOAD_LENGTH(sec_ip, sec_tcp); 595 | tot_clen += sec_clen; 596 | } else { 597 | sec_clen = 0; 598 | tc_log_debug1(LOG_INFO, 0, "no sec auth:%u", ntohs(s->src_port)); 599 | } 600 | 601 | item = hash_find(ctx.ps_table, s->hash_key); 602 | if (item) { 603 | tot_clen += item->tot_cont_len; 604 | } 605 | 606 | tc_log_debug2(LOG_INFO, 0, "total len subtracted:%u,p:%u", tot_clen, 607 | ntohs(s->src_port)); 608 | 609 | mysql_sess->seq_after_ps = ntohl(tcp->seq); 610 | 611 | tcp->seq = htonl(ntohl(tcp->seq) - tot_clen); 612 | fir_tcp->seq = htonl(ntohl(tcp->seq) + 1); 613 | tc_save_pack(s, s->slide_win_packs, fir_ip, fir_tcp); 614 | mysql_sess->auth_packet_already_added = 1; 615 | 616 | if (sec_tcp != NULL) { 617 | sec_tcp->seq = htonl(ntohl(fir_tcp->seq) + fir_clen); 618 | tc_save_pack(s, s->slide_win_packs, sec_ip, sec_tcp); 619 | tc_log_debug1(LOG_INFO, 0, "add sec auth:%u", ntohs(s->src_port)); 620 | } 621 | 622 | base_seq = ntohl(fir_tcp->seq) + fir_clen + sec_clen; 623 | 624 | if (item) { 625 | ln = link_list_first(item->list); 626 | while (ln) { 627 | p = (unsigned char *) ln->data; 628 | t_ip = (tc_iph_t *) (p + ETHERNET_HDR_LEN); 629 | t_tcp = (tc_tcph_t *) ((char *) t_ip + size_ip); 630 | t_tcp->seq = htonl(base_seq); 631 | tc_save_pack(s, s->slide_win_packs, t_ip, t_tcp); 632 | base_seq += TCP_PAYLOAD_LENGTH(t_ip, t_tcp); 633 | ln = link_list_get_next(item->list, ln); 634 | } 635 | } 636 | 637 | return TC_OK; 638 | } 639 | 640 | 641 | static int 642 | proc_when_sess_created(tc_sess_t *s) 643 | { 644 | tc_mysql_session *data = s->data; 645 | 646 | if (data == NULL) { 647 | data = (tc_mysql_session *) tc_pcalloc(s->pool, sizeof(tc_mysql_session)); 648 | 649 | if (data) { 650 | s->data = data; 651 | } 652 | } else { 653 | tc_memzero(data, sizeof(tc_mysql_session)); 654 | } 655 | 656 | return TC_OK; 657 | } 658 | 659 | 660 | static int 661 | proc_when_sess_destroyed(tc_sess_t *s) 662 | { 663 | release_resources(s->hash_key); 664 | return TC_OK; 665 | } 666 | 667 | 668 | static int 669 | proc_greet(tc_sess_t *s, tc_iph_t *ip, tc_tcph_t *tcp) 670 | { 671 | int ret; 672 | uint16_t size_tcp, cont_len; 673 | unsigned char *payload; 674 | tc_mysql_session *mysql_sess; 675 | 676 | mysql_sess = s->data; 677 | tc_log_debug1(LOG_INFO, 0, "recv greet from back:%u", ntohs(s->src_port)); 678 | size_tcp = tcp->doff << 2; 679 | mysql_sess->sec_auth_checked = 0; 680 | payload = (unsigned char *) ((char *) tcp + size_tcp); 681 | tc_memzero(mysql_sess->scramble, SCRAMBLE_LENGTH + 1); 682 | 683 | cont_len = s->cur_pack.cont_len; 684 | 685 | ret = parse_handshake_init_cont(payload, cont_len, mysql_sess->scramble); 686 | if (!ret) { 687 | if (cont_len > 11) { 688 | tc_log_info(LOG_WARN, 0, "port:%u,payload:%s", 689 | ntohs(s->src_port), (char *) (payload + 11)); 690 | } 691 | s->sm.sess_over = 1; 692 | return PACK_STOP; 693 | } 694 | 695 | return PACK_CONTINUE; 696 | } 697 | 698 | 699 | static int 700 | check_needed_for_sec_auth(tc_sess_t *s, tc_iph_t *ip, tc_tcph_t *tcp) 701 | { 702 | uint16_t size_tcp; 703 | unsigned char *payload; 704 | tc_mysql_session *mysql_sess; 705 | 706 | mysql_sess = s->data; 707 | if (mysql_sess->sec_auth_checked == 0) { 708 | size_tcp = tcp->doff << 2; 709 | payload = (unsigned char *) ((char *) tcp + size_tcp); 710 | if (is_last_data_packet(payload)) { 711 | tc_log_debug1(LOG_INFO, 0, "needs sec auth:%u", ntohs(s->src_port)); 712 | mysql_sess->sec_auth_not_yet_done = 1; 713 | } 714 | mysql_sess->sec_auth_checked = 1; 715 | } 716 | 717 | return TC_OK; 718 | } 719 | 720 | 721 | static int 722 | proc_auth(tc_sess_t *s, tc_iph_t *ip, tc_tcph_t *tcp) 723 | { 724 | if (!s->sm.rcv_rep_greet) { 725 | return PACK_STOP; 726 | } 727 | 728 | if (mysql_dispose_auth(s, ip, tcp) == TC_ERR) { 729 | return PACK_STOP; 730 | } 731 | 732 | return PACK_CONTINUE; 733 | } 734 | 735 | 736 | static int 737 | mysql_parse_user_info(tc_conf_t *cf, tc_cmd_t *cmd) 738 | { 739 | char pass[MAX_USER_INFO]; 740 | tc_str_t *user_password; 741 | 742 | user_password = cf->args->elts; 743 | 744 | tc_memzero(pass, MAX_USER_INFO); 745 | 746 | if (user_password[1].len >= MAX_USER_INFO) { 747 | tc_log_info(LOG_ERR, 0, "user password pair too long"); 748 | return TC_ERR; 749 | } 750 | 751 | memcpy(pass, user_password[1].data, user_password[1].len); 752 | 753 | if (retrieve_mysql_user_pwd_info(cf->pool, pass) == -1) { 754 | tc_log_info(LOG_ERR, 0, "parse user password error"); 755 | return TC_ERR; 756 | } 757 | 758 | return TC_OK; 759 | } 760 | 761 | 762 | static tc_cmd_t mysql_commands[] = { 763 | { tc_string("user"), 764 | 0, 765 | 0, 766 | TC_CONF_TAKE1, 767 | mysql_parse_user_info, 768 | NULL 769 | } 770 | }; 771 | 772 | 773 | tc_module_t tc_mysql_module = { 774 | &ctx, 775 | mysql_commands, 776 | init_mysql_module, 777 | exit_mysql_module, 778 | remove_obsolete_resources, 779 | check_renew_session, 780 | prepare_for_renew_session, 781 | check_pack_needed_for_recons, 782 | proc_when_sess_created, 783 | proc_when_sess_destroyed, 784 | release_resources, 785 | proc_greet, 786 | proc_auth, 787 | check_needed_for_sec_auth, 788 | NULL 789 | }; 790 | 791 | --------------------------------------------------------------------------------