1 /*
2    Copyright (c) 2007, 2013, Oracle and/or its affiliates.
3    Copyright (c) 2008, 2020, MariaDB
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; version 2 of the License.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA
17 */
18 
19 /*
20   Functions to autenticate and handle reqests for a connection
21 */
22 
23 #include "mariadb.h"
24 #include "mysqld.h"
25 #include "sql_priv.h"
26 #ifndef __WIN__
27 #include <netdb.h>        // getservbyname, servent
28 #endif
29 #include "sql_audit.h"
30 #include "sql_connect.h"
31 #include "probes_mysql.h"
32 #include "sql_parse.h"                          // sql_command_flags,
33                                                 // execute_init_command,
34                                                 // do_command
35 #include "sql_db.h"                             // mysql_change_db
36 #include "hostname.h" // inc_host_errors, ip_to_hostname,
37                       // reset_host_errors
38 #include "sql_acl.h"  // acl_getroot, NO_ACCESS, SUPER_ACL
39 #include "sql_callback.h"
40 
41 #ifdef WITH_WSREP
42 #include "wsrep_trans_observer.h" /* wsrep open/close */
43 #include "wsrep_mysqld.h"
44 #endif /* WITH_WSREP */
45 #include "proxy_protocol.h"
46 
47 HASH global_user_stats, global_client_stats, global_table_stats;
48 HASH global_index_stats;
49 /* Protects the above global stats */
50 extern mysql_mutex_t LOCK_global_user_client_stats;
51 extern mysql_mutex_t LOCK_global_table_stats;
52 extern mysql_mutex_t LOCK_global_index_stats;
53 extern vio_keepalive_opts opt_vio_keepalive;
54 
55 /*
56   Get structure for logging connection data for the current user
57 */
58 
59 #ifndef NO_EMBEDDED_ACCESS_CHECKS
60 static HASH hash_user_connections;
61 
get_or_create_user_conn(THD * thd,const char * user,const char * host,const USER_RESOURCES * mqh)62 int get_or_create_user_conn(THD *thd, const char *user,
63                             const char *host,
64                             const USER_RESOURCES *mqh)
65 {
66   int return_val= 0;
67   size_t temp_len, user_len;
68   char temp_user[USER_HOST_BUFF_SIZE];
69   struct  user_conn *uc;
70 
71   DBUG_ASSERT(user != 0);
72   DBUG_ASSERT(host != 0);
73   DBUG_ASSERT(thd->user_connect == 0);
74 
75   user_len= strlen(user);
76   temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
77   mysql_mutex_lock(&LOCK_user_conn);
78   if (!(uc = (struct  user_conn *) my_hash_search(&hash_user_connections,
79 					       (uchar*) temp_user, temp_len)))
80   {
81     /* First connection for user; Create a user connection object */
82     if (!(uc= ((struct user_conn*)
83 	       my_malloc(sizeof(struct user_conn) + temp_len+1,
84 			 MYF(MY_WME)))))
85     {
86       /* MY_WME ensures an error is set in THD. */
87       return_val= 1;
88       goto end;
89     }
90     uc->user=(char*) (uc+1);
91     memcpy(uc->user,temp_user,temp_len+1);
92     uc->host= uc->user + user_len +  1;
93     uc->len= (uint)temp_len;
94     uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
95     uc->reset_utime= thd->thr_create_utime;
96     if (my_hash_insert(&hash_user_connections, (uchar*) uc))
97     {
98       /* The only possible error is out of memory, MY_WME sets an error. */
99       my_free(uc);
100       return_val= 1;
101       goto end;
102     }
103   }
104   uc->user_resources= *mqh;
105   thd->user_connect=uc;
106   uc->connections++;
107 end:
108   mysql_mutex_unlock(&LOCK_user_conn);
109   return return_val;
110 }
111 
112 
113 /*
114   check if user has already too many connections
115 
116   SYNOPSIS
117   check_for_max_user_connections()
118   thd			Thread handle
119   uc			User connect object
120 
121   NOTES
122     If check fails, we decrease user connection count, which means one
123     shouldn't call decrease_user_connections() after this function.
124 
125   RETURN
126     0	ok
127     1	error
128 */
129 
check_for_max_user_connections(THD * thd,USER_CONN * uc)130 int check_for_max_user_connections(THD *thd, USER_CONN *uc)
131 {
132   int error= 1;
133   Host_errors errors;
134   DBUG_ENTER("check_for_max_user_connections");
135 
136   mysql_mutex_lock(&LOCK_user_conn);
137 
138   /* Root is not affected by the value of max_user_connections */
139   if (global_system_variables.max_user_connections &&
140       !uc->user_resources.user_conn &&
141       global_system_variables.max_user_connections < uc->connections &&
142       !(thd->security_ctx->master_access & SUPER_ACL))
143   {
144     my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
145     error=1;
146     errors.m_max_user_connection= 1;
147     goto end;
148   }
149   time_out_user_resource_limits(thd, uc);
150   if (uc->user_resources.user_conn &&
151       uc->user_resources.user_conn < uc->connections)
152   {
153     my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
154              "max_user_connections",
155              (long) uc->user_resources.user_conn);
156     error= 1;
157     errors.m_max_user_connection= 1;
158     goto end;
159   }
160   if (uc->user_resources.conn_per_hour &&
161       uc->user_resources.conn_per_hour <= uc->conn_per_hour)
162   {
163     my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
164              "max_connections_per_hour",
165              (long) uc->user_resources.conn_per_hour);
166     error=1;
167     errors.m_max_user_connection_per_hour= 1;
168     goto end;
169   }
170   uc->conn_per_hour++;
171   error= 0;
172 
173 end:
174   if (unlikely(error))
175   {
176     uc->connections--; // no need for decrease_user_connections() here
177     /*
178       The thread may returned back to the pool and assigned to a user
179       that doesn't have a limit. Ensure the user is not using resources
180       of someone else.
181     */
182     thd->user_connect= NULL;
183   }
184   mysql_mutex_unlock(&LOCK_user_conn);
185   if (unlikely(error))
186   {
187     inc_host_errors(thd->main_security_ctx.ip, &errors);
188   }
189   DBUG_RETURN(error);
190 }
191 
192 
193 /*
194   Decrease user connection count
195 
196   SYNOPSIS
197     decrease_user_connections()
198     uc			User connection object
199 
200   NOTES
201     If there is a n user connection object for a connection
202     (which only happens if 'max_user_connections' is defined or
203     if someone has created a resource grant for a user), then
204     the connection count is always incremented on connect.
205 
206     The user connect object is not freed if some users has
207     'max connections per hour' defined as we need to be able to hold
208     count over the lifetime of the connection.
209 */
210 
decrease_user_connections(USER_CONN * uc)211 void decrease_user_connections(USER_CONN *uc)
212 {
213   DBUG_ENTER("decrease_user_connections");
214   mysql_mutex_lock(&LOCK_user_conn);
215   DBUG_ASSERT(uc->connections);
216   if (!--uc->connections && !mqh_used)
217   {
218     /* Last connection for user; Delete it */
219     (void) my_hash_delete(&hash_user_connections,(uchar*) uc);
220   }
221   mysql_mutex_unlock(&LOCK_user_conn);
222   DBUG_VOID_RETURN;
223 }
224 
225 
226 /*
227   Reset per-hour user resource limits when it has been more than
228   an hour since they were last checked
229 
230   SYNOPSIS:
231     time_out_user_resource_limits()
232     thd			Thread handler
233     uc			User connection details
234 
235   NOTE:
236     This assumes that the LOCK_user_conn mutex has been acquired, so it is
237     safe to test and modify members of the USER_CONN structure.
238 */
239 
time_out_user_resource_limits(THD * thd,USER_CONN * uc)240 void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
241 {
242   ulonglong check_time= thd->start_utime;
243   DBUG_ENTER("time_out_user_resource_limits");
244 
245   /* If more than a hour since last check, reset resource checking */
246   if (check_time  - uc->reset_utime >= 3600000000ULL)
247   {
248     uc->questions=0;
249     uc->updates=0;
250     uc->conn_per_hour=0;
251     uc->reset_utime= check_time;
252   }
253 
254   DBUG_VOID_RETURN;
255 }
256 
257 /*
258   Check if maximum queries per hour limit has been reached
259   returns 0 if OK.
260 */
261 
check_mqh(THD * thd,uint check_command)262 bool check_mqh(THD *thd, uint check_command)
263 {
264   bool error= 0;
265   USER_CONN *uc=thd->user_connect;
266   DBUG_ENTER("check_mqh");
267   DBUG_ASSERT(uc != 0);
268 
269   mysql_mutex_lock(&LOCK_user_conn);
270 
271   time_out_user_resource_limits(thd, uc);
272 
273   /* Check that we have not done too many questions / hour */
274   if (uc->user_resources.questions &&
275       uc->questions++ >= uc->user_resources.questions)
276   {
277     my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_queries_per_hour",
278              (long) uc->user_resources.questions);
279     error=1;
280     goto end;
281   }
282   if (check_command < (uint) SQLCOM_END)
283   {
284     /* Check that we have not done too many updates / hour */
285     if (uc->user_resources.updates &&
286         (sql_command_flags[check_command] & CF_CHANGES_DATA) &&
287 	uc->updates++ >= uc->user_resources.updates)
288     {
289       my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates_per_hour",
290                (long) uc->user_resources.updates);
291       error=1;
292       goto end;
293     }
294   }
295 end:
296   mysql_mutex_unlock(&LOCK_user_conn);
297   DBUG_RETURN(error);
298 }
299 
300 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
301 
302 /*
303   Check for maximum allowable user connections, if the mysqld server is
304   started with corresponding variable that is greater then 0.
305 */
306 
get_key_conn(user_conn * buff,size_t * length,my_bool not_used)307 extern "C" uchar *get_key_conn(user_conn *buff, size_t *length,
308 			      my_bool not_used __attribute__((unused)))
309 {
310   *length= buff->len;
311   return (uchar*) buff->user;
312 }
313 
314 
free_user(struct user_conn * uc)315 extern "C" void free_user(struct user_conn *uc)
316 {
317   my_free(uc);
318 }
319 
320 
init_max_user_conn(void)321 void init_max_user_conn(void)
322 {
323 #ifndef NO_EMBEDDED_ACCESS_CHECKS
324   my_hash_init(&hash_user_connections, system_charset_info, max_connections,
325                0, 0, (my_hash_get_key) get_key_conn,
326                (my_hash_free_key) free_user, 0);
327 #endif
328 }
329 
330 
free_max_user_conn(void)331 void free_max_user_conn(void)
332 {
333 #ifndef NO_EMBEDDED_ACCESS_CHECKS
334   my_hash_free(&hash_user_connections);
335 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
336 }
337 
338 
reset_mqh(LEX_USER * lu,bool get_them=0)339 void reset_mqh(LEX_USER *lu, bool get_them= 0)
340 {
341 #ifndef NO_EMBEDDED_ACCESS_CHECKS
342   mysql_mutex_lock(&LOCK_user_conn);
343   if (lu)  // for GRANT
344   {
345     USER_CONN *uc;
346     size_t temp_len=lu->user.length+lu->host.length+2;
347     char temp_user[USER_HOST_BUFF_SIZE];
348 
349     memcpy(temp_user,lu->user.str,lu->user.length);
350     memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
351     temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
352     if ((uc = (struct  user_conn *) my_hash_search(&hash_user_connections,
353                                                    (uchar*) temp_user,
354                                                    temp_len)))
355     {
356       uc->questions=0;
357       get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
358       uc->updates=0;
359       uc->conn_per_hour=0;
360     }
361   }
362   else
363   {
364     /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
365     for (uint idx=0;idx < hash_user_connections.records; idx++)
366     {
367       USER_CONN *uc=(struct user_conn *)
368         my_hash_element(&hash_user_connections, idx);
369       if (get_them)
370 	get_mqh(uc->user,uc->host,uc);
371       uc->questions=0;
372       uc->updates=0;
373       uc->conn_per_hour=0;
374     }
375   }
376   mysql_mutex_unlock(&LOCK_user_conn);
377 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
378 }
379 
380 /*****************************************************************************
381  Handle users statistics
382 *****************************************************************************/
383 
384 /* 'mysql_system_user' is used for when the user is not defined for a THD. */
385 static const char mysql_system_user[]= "#mysql_system#";
386 
387 // Returns 'user' if it's not NULL.  Returns 'mysql_system_user' otherwise.
get_valid_user_string(const char * user)388 static const char * get_valid_user_string(const char* user)
389 {
390   return user ? user : mysql_system_user;
391 }
392 
393 /*
394   Returns string as 'IP' for the client-side of the connection represented by
395   'client'. Does not allocate memory. May return "".
396 */
397 
get_client_host(THD * client)398 static const char *get_client_host(THD *client)
399 {
400   return client->security_ctx->host_or_ip[0] ?
401     client->security_ctx->host_or_ip :
402     client->security_ctx->host ? client->security_ctx->host : "";
403 }
404 
get_key_user_stats(USER_STATS * user_stats,size_t * length,my_bool not_used)405 extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
406                                      my_bool not_used __attribute__((unused)))
407 {
408   *length= user_stats->user_name_length;
409   return (uchar*) user_stats->user;
410 }
411 
free_user_stats(USER_STATS * user_stats)412 void free_user_stats(USER_STATS* user_stats)
413 {
414   my_free(user_stats);
415 }
416 
init_user_stats(USER_STATS * user_stats,const char * user,size_t user_length,const char * priv_user,uint total_connections,uint total_ssl_connections,uint concurrent_connections,time_t connected_time,double busy_time,double cpu_time,ulonglong bytes_received,ulonglong bytes_sent,ulonglong binlog_bytes_written,ha_rows rows_sent,ha_rows rows_read,ha_rows rows_inserted,ha_rows rows_deleted,ha_rows rows_updated,ulonglong select_commands,ulonglong update_commands,ulonglong other_commands,ulonglong commit_trans,ulonglong rollback_trans,ulonglong denied_connections,ulonglong lost_connections,ulonglong max_statement_time_exceeded,ulonglong access_denied_errors,ulonglong empty_queries)417 void init_user_stats(USER_STATS *user_stats,
418                      const char *user,
419                      size_t user_length,
420                      const char *priv_user,
421                      uint total_connections,
422                      uint total_ssl_connections,
423                      uint concurrent_connections,
424                      time_t connected_time,
425                      double busy_time,
426                      double cpu_time,
427                      ulonglong bytes_received,
428                      ulonglong bytes_sent,
429                      ulonglong binlog_bytes_written,
430                      ha_rows rows_sent,
431                      ha_rows rows_read,
432                      ha_rows rows_inserted,
433                      ha_rows rows_deleted,
434                      ha_rows rows_updated,
435                      ulonglong select_commands,
436                      ulonglong update_commands,
437                      ulonglong other_commands,
438                      ulonglong commit_trans,
439                      ulonglong rollback_trans,
440                      ulonglong denied_connections,
441                      ulonglong lost_connections,
442                      ulonglong max_statement_time_exceeded,
443                      ulonglong access_denied_errors,
444                      ulonglong empty_queries)
445 {
446   DBUG_ENTER("init_user_stats");
447   DBUG_PRINT("enter", ("user: %s  priv_user: %s", user, priv_user));
448 
449   user_length= MY_MIN(user_length, sizeof(user_stats->user)-1);
450   memcpy(user_stats->user, user, user_length);
451   user_stats->user[user_length]= 0;
452   user_stats->user_name_length= (uint)user_length;
453   strmake_buf(user_stats->priv_user, priv_user);
454 
455   user_stats->total_connections= total_connections;
456   user_stats->total_ssl_connections=  total_ssl_connections;
457   user_stats->concurrent_connections= concurrent_connections;
458   user_stats->connected_time= connected_time;
459   user_stats->busy_time= busy_time;
460   user_stats->cpu_time= cpu_time;
461   user_stats->bytes_received= bytes_received;
462   user_stats->bytes_sent= bytes_sent;
463   user_stats->binlog_bytes_written= binlog_bytes_written;
464   user_stats->rows_sent= rows_sent;
465   user_stats->rows_read= rows_read;
466   user_stats->rows_inserted= rows_inserted;
467   user_stats->rows_deleted= rows_deleted;
468   user_stats->rows_updated= rows_updated;
469   user_stats->select_commands= select_commands;
470   user_stats->update_commands= update_commands;
471   user_stats->other_commands= other_commands;
472   user_stats->commit_trans= commit_trans;
473   user_stats->rollback_trans= rollback_trans;
474   user_stats->denied_connections= denied_connections;
475   user_stats->lost_connections= lost_connections;
476   user_stats->max_statement_time_exceeded= max_statement_time_exceeded;
477   user_stats->access_denied_errors= access_denied_errors;
478   user_stats->empty_queries= empty_queries;
479   DBUG_VOID_RETURN;
480 }
481 
482 
init_global_user_stats(void)483 void init_global_user_stats(void)
484 {
485   my_hash_init(&global_user_stats, system_charset_info, max_connections,
486                0, 0, (my_hash_get_key) get_key_user_stats,
487                (my_hash_free_key) free_user_stats, 0);
488 }
489 
init_global_client_stats(void)490 void init_global_client_stats(void)
491 {
492   my_hash_init(&global_client_stats, system_charset_info, max_connections,
493                0, 0, (my_hash_get_key) get_key_user_stats,
494                (my_hash_free_key) free_user_stats, 0);
495 }
496 
get_key_table_stats(TABLE_STATS * table_stats,size_t * length,my_bool not_used)497 extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
498                                       my_bool not_used __attribute__((unused)))
499 {
500   *length= table_stats->table_name_length;
501   return (uchar*) table_stats->table;
502 }
503 
free_table_stats(TABLE_STATS * table_stats)504 extern "C" void free_table_stats(TABLE_STATS* table_stats)
505 {
506   my_free(table_stats);
507 }
508 
init_global_table_stats(void)509 void init_global_table_stats(void)
510 {
511   my_hash_init(&global_table_stats, system_charset_info, max_connections,
512                0, 0, (my_hash_get_key) get_key_table_stats,
513                (my_hash_free_key) free_table_stats, 0);
514 }
515 
get_key_index_stats(INDEX_STATS * index_stats,size_t * length,my_bool not_used)516 extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
517                                      my_bool not_used __attribute__((unused)))
518 {
519   *length= index_stats->index_name_length;
520   return (uchar*) index_stats->index;
521 }
522 
free_index_stats(INDEX_STATS * index_stats)523 extern "C" void free_index_stats(INDEX_STATS* index_stats)
524 {
525   my_free(index_stats);
526 }
527 
init_global_index_stats(void)528 void init_global_index_stats(void)
529 {
530   my_hash_init(&global_index_stats, system_charset_info, max_connections,
531                0, 0, (my_hash_get_key) get_key_index_stats,
532                (my_hash_free_key) free_index_stats, 0);
533 }
534 
535 
free_global_user_stats(void)536 void free_global_user_stats(void)
537 {
538   my_hash_free(&global_user_stats);
539 }
540 
free_global_table_stats(void)541 void free_global_table_stats(void)
542 {
543   my_hash_free(&global_table_stats);
544 }
545 
free_global_index_stats(void)546 void free_global_index_stats(void)
547 {
548   my_hash_free(&global_index_stats);
549 }
550 
free_global_client_stats(void)551 void free_global_client_stats(void)
552 {
553   my_hash_free(&global_client_stats);
554 }
555 
556 /*
557   Increments the global stats connection count for an entry from
558   global_client_stats or global_user_stats. Returns 0 on success
559   and 1 on error.
560 */
561 
increment_count_by_name(const char * name,size_t name_length,const char * role_name,HASH * users_or_clients,THD * thd)562 static bool increment_count_by_name(const char *name, size_t name_length,
563                                    const char *role_name,
564                                    HASH *users_or_clients, THD *thd)
565 {
566   USER_STATS *user_stats;
567 
568   if (!(user_stats= (USER_STATS*) my_hash_search(users_or_clients, (uchar*) name,
569                                               name_length)))
570   {
571     /* First connection for this user or client */
572     if (!(user_stats= ((USER_STATS*)
573                        my_malloc(sizeof(USER_STATS),
574                                  MYF(MY_WME | MY_ZEROFILL)))))
575       return TRUE;                              // Out of memory
576 
577     init_user_stats(user_stats, name, name_length, role_name,
578                     0, 0, 0,   // connections
579                     0, 0, 0,   // time
580                     0, 0, 0,   // bytes sent, received and written
581                     0, 0,      // rows sent and read
582                     0, 0, 0,   // rows inserted, deleted and updated
583                     0, 0, 0,   // select, update and other commands
584                     0, 0,      // commit and rollback trans
585                     thd->status_var.access_denied_errors,
586                     0,         // lost connections
587                     0,         // max query timeouts
588                     0,         // access denied errors
589                     0);        // empty queries
590 
591     if (my_hash_insert(users_or_clients, (uchar*)user_stats))
592     {
593       my_free(user_stats);
594       return TRUE;                              // Out of memory
595     }
596   }
597   user_stats->total_connections++;
598   if (thd->net.vio && thd->net.vio->type == VIO_TYPE_SSL)
599     user_stats->total_ssl_connections++;
600   return FALSE;
601 }
602 
603 
604 /*
605   Increments the global user and client stats connection count.
606 
607   @param use_lock  if true, LOCK_global_user_client_stats will be locked
608 
609   @retval 0 ok
610   @retval 1 error.
611 */
612 
613 #ifndef EMBEDDED_LIBRARY
increment_connection_count(THD * thd,bool use_lock)614 static bool increment_connection_count(THD* thd, bool use_lock)
615 {
616   const char *user_string= get_valid_user_string(thd->main_security_ctx.user);
617   const char *client_string= get_client_host(thd);
618   bool return_value= FALSE;
619 
620   if (!thd->userstat_running)
621     return FALSE;
622 
623   if (use_lock)
624     mysql_mutex_lock(&LOCK_global_user_client_stats);
625 
626   if (increment_count_by_name(user_string, strlen(user_string), user_string,
627                               &global_user_stats, thd))
628   {
629     return_value= TRUE;
630     goto end;
631   }
632   if (increment_count_by_name(client_string, strlen(client_string),
633                               user_string, &global_client_stats, thd))
634   {
635     return_value= TRUE;
636     goto end;
637   }
638 
639 end:
640   if (use_lock)
641     mysql_mutex_unlock(&LOCK_global_user_client_stats);
642   return return_value;
643 }
644 #endif
645 
646 /*
647   Used to update the global user and client stats
648 */
649 
update_global_user_stats_with_user(THD * thd,USER_STATS * user_stats,time_t now)650 static void update_global_user_stats_with_user(THD *thd,
651                                                USER_STATS *user_stats,
652                                                time_t now)
653 {
654   DBUG_ASSERT(thd->userstat_running);
655 
656   user_stats->connected_time+= now - thd->last_global_update_time;
657   user_stats->busy_time+=  (thd->status_var.busy_time -
658                             thd->org_status_var.busy_time);
659   user_stats->cpu_time+=   (thd->status_var.cpu_time -
660                             thd->org_status_var.cpu_time);
661   /*
662     This is handle specially as bytes_received is incremented BEFORE
663     org_status_var is copied.
664   */
665   user_stats->bytes_received+= (thd->org_status_var.bytes_received-
666                                 thd->start_bytes_received);
667   user_stats->bytes_sent+= (thd->status_var.bytes_sent -
668                             thd->org_status_var.bytes_sent);
669   user_stats->binlog_bytes_written+=
670     (thd->status_var.binlog_bytes_written -
671      thd->org_status_var.binlog_bytes_written);
672   /* We are not counting rows in internal temporary tables here ! */
673   user_stats->rows_read+=      (thd->status_var.rows_read -
674                                 thd->org_status_var.rows_read);
675   user_stats->rows_sent+=      (thd->status_var.rows_sent -
676                                 thd->org_status_var.rows_sent);
677   user_stats->rows_inserted+=  (thd->status_var.ha_write_count -
678                                 thd->org_status_var.ha_write_count);
679   user_stats->rows_deleted+=   (thd->status_var.ha_delete_count -
680                                 thd->org_status_var.ha_delete_count);
681   user_stats->rows_updated+=   (thd->status_var.ha_update_count -
682                                 thd->org_status_var.ha_update_count);
683   user_stats->select_commands+= thd->select_commands;
684   user_stats->update_commands+= thd->update_commands;
685   user_stats->other_commands+=  thd->other_commands;
686   user_stats->commit_trans+=   (thd->status_var.ha_commit_count -
687                                 thd->org_status_var.ha_commit_count);
688   user_stats->rollback_trans+= (thd->status_var.ha_rollback_count +
689                                 thd->status_var.ha_savepoint_rollback_count -
690                                 thd->org_status_var.ha_rollback_count -
691                                 thd->org_status_var.
692                                 ha_savepoint_rollback_count);
693   user_stats->access_denied_errors+=
694     (thd->status_var.access_denied_errors -
695      thd->org_status_var.access_denied_errors);
696   user_stats->empty_queries+=   (thd->status_var.empty_queries -
697                                  thd->org_status_var.empty_queries);
698 
699   /* The following can only contain 0 or 1 and then connection ends */
700   user_stats->denied_connections+= thd->status_var.access_denied_errors;
701   user_stats->lost_connections+=   thd->status_var.lost_connections;
702   user_stats->max_statement_time_exceeded+= thd->status_var.max_statement_time_exceeded;
703 }
704 
705 
706 /*  Updates the global stats of a user or client */
update_global_user_stats(THD * thd,bool create_user,time_t now)707 void update_global_user_stats(THD *thd, bool create_user, time_t now)
708 {
709   const char *user_string, *client_string;
710   USER_STATS *user_stats;
711   size_t user_string_length, client_string_length;
712   DBUG_ASSERT(thd->userstat_running);
713 
714   user_string= get_valid_user_string(thd->main_security_ctx.user);
715   user_string_length= strlen(user_string);
716   client_string= get_client_host(thd);
717   client_string_length= strlen(client_string);
718 
719   mysql_mutex_lock(&LOCK_global_user_client_stats);
720 
721   // Update by user name
722   if ((user_stats= (USER_STATS*) my_hash_search(&global_user_stats,
723                                              (uchar*) user_string,
724                                              user_string_length)))
725   {
726     /* Found user. */
727     update_global_user_stats_with_user(thd, user_stats, now);
728   }
729   else
730   {
731     /* Create the entry */
732     if (create_user)
733     {
734       increment_count_by_name(user_string, user_string_length, user_string,
735                               &global_user_stats, thd);
736     }
737   }
738 
739   /* Update by client IP */
740   if ((user_stats= (USER_STATS*)my_hash_search(&global_client_stats,
741                                             (uchar*) client_string,
742                                             client_string_length)))
743   {
744     // Found by client IP
745     update_global_user_stats_with_user(thd, user_stats, now);
746   }
747   else
748   {
749     // Create the entry
750     if (create_user)
751     {
752       increment_count_by_name(client_string, client_string_length,
753                               user_string, &global_client_stats, thd);
754     }
755   }
756   /* Reset variables only used for counting */
757   thd->select_commands= thd->update_commands= thd->other_commands= 0;
758   thd->last_global_update_time= now;
759 
760   mysql_mutex_unlock(&LOCK_global_user_client_stats);
761 }
762 
763 
764 /**
765   Set thread character set variables from the given ID
766 
767   @param  thd         thread handle
768   @param  cs_number   character set and collation ID
769 
770   @retval  0  OK; character_set_client, collation_connection and
771               character_set_results are set to the new value,
772               or to the default global values.
773 
774   @retval  1  error, e.g. the given ID is not supported by parser.
775               Corresponding SQL error is sent.
776 */
777 
thd_init_client_charset(THD * thd,uint cs_number)778 bool thd_init_client_charset(THD *thd, uint cs_number)
779 {
780   CHARSET_INFO *cs;
781   /*
782    Use server character set and collation if
783    - opt_character_set_client_handshake is not set
784    - client has not specified a character set
785    - client character set doesn't exists in server
786   */
787   if (!opt_character_set_client_handshake ||
788       !(cs= get_charset(cs_number, MYF(0))))
789   {
790     thd->update_charset(global_system_variables.character_set_client,
791                         global_system_variables.collation_connection,
792                         global_system_variables.character_set_results);
793   }
794   else
795   {
796     if (!is_supported_parser_charset(cs))
797     {
798       /* Disallow non-supported parser character sets: UCS2, UTF16, UTF32 */
799       my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
800                cs->csname);
801       return true;
802     }
803     thd->org_charset= cs;
804     thd->update_charset(cs,cs,cs);
805   }
806   return false;
807 }
808 
809 
810 /*
811   Initialize connection threads
812 */
813 
814 #ifndef EMBEDDED_LIBRARY
init_new_connection_handler_thread()815 bool init_new_connection_handler_thread()
816 {
817   pthread_detach_this_thread();
818   if (my_thread_init())
819   {
820     statistic_increment(aborted_connects,&LOCK_status);
821     statistic_increment(connection_errors_internal, &LOCK_status);
822     return 1;
823   }
824   DBUG_EXECUTE_IF("simulate_failed_connection_1", return(1); );
825   return 0;
826 }
827 
828 /**
829   Set client address during authentication.
830 
831   Initializes THD::main_security_ctx and THD::peer_port.
832   Optionally does ip to hostname translation.
833 
834   @param thd   current THD handle
835   @param addr  peer address (can be NULL, if 'ip' is set)
836   @param ip    peer address as string (can be NULL if 'addr' is set)
837   @param port  peer port
838   @param check_proxy_networks if true, and host is in
839                'proxy_protocol_networks' list, skip
840                "host not privileged" check
841   @param[out] host_errors - number of connect
842               errors for this host
843 
844   @retval 0 ok, 1 error
845 */
thd_set_peer_addr(THD * thd,sockaddr_storage * addr,const char * ip,uint port,bool check_proxy_networks,uint * host_errors)846 int thd_set_peer_addr(THD *thd,
847   sockaddr_storage *addr,
848   const char *ip,
849   uint port,
850   bool check_proxy_networks,
851   uint *host_errors)
852 {
853   *host_errors= 0;
854 
855   thd->peer_port= port;
856 
857   char ip_string[128];
858   if (!ip)
859   {
860     void *addr_data;
861     if (addr->ss_family == AF_UNIX)
862     {
863         /* local connection */
864         my_free((void *)thd->main_security_ctx.ip);
865         thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host = my_localhost;
866         thd->main_security_ctx.ip= 0;
867         return 0;
868     }
869     else if (addr->ss_family == AF_INET)
870       addr_data= &((struct sockaddr_in *)addr)->sin_addr;
871     else
872       addr_data= &((struct sockaddr_in6 *)addr)->sin6_addr;
873     if (!inet_ntop(addr->ss_family,addr_data, ip_string, sizeof(ip_string)))
874     {
875       DBUG_ASSERT(0);
876       return 1;
877     }
878     ip= ip_string;
879   }
880 
881   my_free((void *)thd->main_security_ctx.ip);
882   if (!(thd->main_security_ctx.ip = my_strdup(ip, MYF(MY_WME))))
883   {
884     /*
885     No error accounting per IP in host_cache,
886     this is treated as a global server OOM error.
887     TODO: remove the need for my_strdup.
888     */
889     statistic_increment(aborted_connects, &LOCK_status);
890     statistic_increment(connection_errors_internal, &LOCK_status);
891     return 1; /* The error is set by my_strdup(). */
892   }
893   thd->main_security_ctx.host_or_ip = thd->main_security_ctx.ip;
894   if (!(specialflag & SPECIAL_NO_RESOLVE))
895   {
896     int rc;
897 
898     rc = ip_to_hostname(addr,
899       thd->main_security_ctx.ip,
900       &thd->main_security_ctx.host,
901       host_errors);
902 
903     /* Cut very long hostnames to avoid possible overflows */
904     if (thd->main_security_ctx.host)
905     {
906       if (thd->main_security_ctx.host != my_localhost)
907         ((char*)thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host),
908           HOSTNAME_LENGTH)] = 0;
909       thd->main_security_ctx.host_or_ip = thd->main_security_ctx.host;
910     }
911 
912     if (rc == RC_BLOCKED_HOST)
913     {
914       /* HOST_CACHE stats updated by ip_to_hostname(). */
915       my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
916       return 1;
917     }
918   }
919   DBUG_PRINT("info", ("Host: %s  ip: %s",
920     (thd->main_security_ctx.host ?
921       thd->main_security_ctx.host : "unknown host"),
922       (thd->main_security_ctx.ip ?
923         thd->main_security_ctx.ip : "unknown ip")));
924   if ((!check_proxy_networks || !is_proxy_protocol_allowed((struct sockaddr *) addr))
925       && acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
926   {
927     /* HOST_CACHE stats updated by acl_check_host(). */
928     my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
929       thd->main_security_ctx.host_or_ip);
930     return 1;
931   }
932   return 0;
933 }
934 
935 /*
936   Perform handshake, authorize client and update thd ACL variables.
937 
938   SYNOPSIS
939     check_connection()
940     thd  thread handle
941 
942   RETURN
943      0  success, thd is updated.
944      1  error
945 */
946 
check_connection(THD * thd)947 static int check_connection(THD *thd)
948 {
949   uint connect_errors= 0;
950   int auth_rc;
951   NET *net= &thd->net;
952 
953   DBUG_PRINT("info",
954              ("New connection received on %s", vio_description(net->vio)));
955 
956 #ifdef SIGNAL_WITH_VIO_CLOSE
957   thd->set_active_vio(net->vio);
958 #endif
959 
960   if (!thd->main_security_ctx.host)         // If TCP/IP connection
961   {
962     my_bool peer_rc;
963     char ip[NI_MAXHOST];
964     uint16 peer_port;
965 
966     peer_rc= vio_peer_addr(net->vio, ip, &peer_port, NI_MAXHOST);
967 
968     /*
969     ===========================================================================
970     DEBUG code only (begin)
971     Simulate various output from vio_peer_addr().
972     ===========================================================================
973     */
974 
975     DBUG_EXECUTE_IF("vio_peer_addr_error",
976                     {
977                       peer_rc= 1;
978                     }
979                     );
980     DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv4",
981                     {
982                       struct sockaddr *sa= (sockaddr *) &net->vio->remote;
983                       sa->sa_family= AF_INET;
984                       struct in_addr *ip4= &((struct sockaddr_in *) sa)->sin_addr;
985                       /* See RFC 5737, 192.0.2.0/24 is reserved. */
986                       const char* fake= "192.0.2.4";
987                       inet_pton(AF_INET,fake, ip4);
988                       strcpy(ip, fake);
989                       peer_rc= 0;
990                     }
991                     );
992 
993 #ifdef HAVE_IPV6
994     DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv6",
995                     {
996                       struct sockaddr_in6 *sa= (sockaddr_in6 *) &net->vio->remote;
997                       sa->sin6_family= AF_INET6;
998                       struct in6_addr *ip6= & sa->sin6_addr;
999                       /* See RFC 3849, ipv6 2001:DB8::/32 is reserved. */
1000                       const char* fake= "2001:db8::6:6";
1001                       /* inet_pton(AF_INET6, fake, ip6); not available on Windows XP. */
1002                       ip6->s6_addr[ 0] = 0x20;
1003                       ip6->s6_addr[ 1] = 0x01;
1004                       ip6->s6_addr[ 2] = 0x0d;
1005                       ip6->s6_addr[ 3] = 0xb8;
1006                       ip6->s6_addr[ 4] = 0x00;
1007                       ip6->s6_addr[ 5] = 0x00;
1008                       ip6->s6_addr[ 6] = 0x00;
1009                       ip6->s6_addr[ 7] = 0x00;
1010                       ip6->s6_addr[ 8] = 0x00;
1011                       ip6->s6_addr[ 9] = 0x00;
1012                       ip6->s6_addr[10] = 0x00;
1013                       ip6->s6_addr[11] = 0x00;
1014                       ip6->s6_addr[12] = 0x00;
1015                       ip6->s6_addr[13] = 0x06;
1016                       ip6->s6_addr[14] = 0x00;
1017                       ip6->s6_addr[15] = 0x06;
1018                       strcpy(ip, fake);
1019                       peer_rc= 0;
1020                     }
1021                     );
1022 #endif /* HAVE_IPV6 */
1023 
1024     /*
1025     ===========================================================================
1026     DEBUG code only (end)
1027     ===========================================================================
1028     */
1029 
1030     if (peer_rc)
1031     {
1032       /*
1033         Since we can not even get the peer IP address,
1034         there is nothing to show in the host_cache,
1035         so increment the global status variable for peer address errors.
1036       */
1037       statistic_increment(connection_errors_peer_addr, &LOCK_status);
1038       my_error(ER_BAD_HOST_ERROR, MYF(0));
1039       statistic_increment(aborted_connects_preauth, &LOCK_status);
1040       return 1;
1041     }
1042 
1043     if (thd_set_peer_addr(thd, &net->vio->remote, ip, peer_port,
1044                           true, &connect_errors))
1045     {
1046       statistic_increment(aborted_connects_preauth, &LOCK_status);
1047       return 1;
1048     }
1049   }
1050   else /* Hostname given means that the connection was on a socket */
1051   {
1052     DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host));
1053     thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
1054     thd->main_security_ctx.ip= 0;
1055     /* Reset sin_addr */
1056     bzero((char*) &net->vio->remote, sizeof(net->vio->remote));
1057   }
1058   vio_keepalive(net->vio, TRUE);
1059   vio_set_keepalive_options(net->vio, &opt_vio_keepalive);
1060 
1061   if (unlikely(thd->packet.alloc(thd->variables.net_buffer_length)))
1062   {
1063     /*
1064       Important note:
1065       net_buffer_length is a SESSION variable,
1066       so it may be tempting to account OOM conditions per IP in the HOST_CACHE,
1067       in case some clients are more demanding than others ...
1068       However, this session variable is *not* initialized with a per client
1069       value during the initial connection, it is initialized from the
1070       GLOBAL net_buffer_length variable from the server.
1071       Hence, there is no reason to account on OOM conditions per client IP,
1072       we count failures in the global server status instead.
1073     */
1074     statistic_increment(aborted_connects,&LOCK_status);
1075     statistic_increment(connection_errors_internal, &LOCK_status);
1076     statistic_increment(aborted_connects_preauth, &LOCK_status);
1077     return 1; /* The error is set by alloc(). */
1078   }
1079 
1080   auth_rc= acl_authenticate(thd, 0);
1081   if (auth_rc == 0 && connect_errors != 0)
1082   {
1083     /*
1084       A client connection from this IP was successful,
1085       after some previous failures.
1086       Reset the connection error counter.
1087     */
1088     reset_host_connect_errors(thd->main_security_ctx.ip);
1089   }
1090 
1091   return auth_rc;
1092 }
1093 
1094 
1095 /*
1096   Setup thread to be used with the current thread
1097 
1098   SYNOPSIS
1099     bool setup_connection_thread_globals()
1100     thd    Thread/connection handler
1101 
1102   RETURN
1103     0   ok
1104     1   Error (out of memory)
1105         In this case we will close the connection and increment status
1106 */
1107 
setup_connection_thread_globals(THD * thd)1108 bool setup_connection_thread_globals(THD *thd)
1109 {
1110   if (thd->store_globals())
1111   {
1112     close_connection(thd, ER_OUT_OF_RESOURCES);
1113     statistic_increment(aborted_connects,&LOCK_status);
1114     statistic_increment(connection_errors_internal, &LOCK_status);
1115     thd->scheduler->end_thread(thd, 0);
1116     return 1;                                   // Error
1117   }
1118   return 0;
1119 }
1120 
1121 
1122 /*
1123   Autenticate user, with error reporting
1124 
1125   SYNOPSIS
1126    login_connection()
1127    thd        Thread handler
1128 
1129   NOTES
1130     Connection is not closed in case of errors
1131 
1132   RETURN
1133     0    ok
1134     1    error
1135 */
1136 
login_connection(THD * thd)1137 bool login_connection(THD *thd)
1138 {
1139   NET *net= &thd->net;
1140   int error= 0;
1141   DBUG_ENTER("login_connection");
1142   DBUG_PRINT("info", ("login_connection called by thread %lu",
1143                       (ulong) thd->thread_id));
1144 
1145   /* Use "connect_timeout" value during connection phase */
1146   my_net_set_read_timeout(net, connect_timeout);
1147   my_net_set_write_timeout(net, connect_timeout);
1148 
1149   error= check_connection(thd);
1150   thd->protocol->end_statement();
1151 
1152   if (unlikely(error))
1153   {						// Wrong permissions
1154 #ifdef _WIN32
1155     if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
1156       my_sleep(1000);				/* must wait after eof() */
1157 #endif
1158     statistic_increment(aborted_connects,&LOCK_status);
1159     error=1;
1160     goto exit;
1161   }
1162   /* Connect completed, set read/write timeouts back to default */
1163   my_net_set_read_timeout(net, thd->variables.net_read_timeout);
1164   my_net_set_write_timeout(net, thd->variables.net_write_timeout);
1165 
1166   /*  Updates global user connection stats. */
1167   if (increment_connection_count(thd, TRUE))
1168   {
1169     my_error(ER_OUTOFMEMORY, MYF(0), (int) (2*sizeof(USER_STATS)));
1170     error= 1;
1171     goto exit;
1172   }
1173 
1174 exit:
1175   mysql_audit_notify_connection_connect(thd);
1176   DBUG_RETURN(error);
1177 }
1178 
1179 
1180 /*
1181   Close an established connection
1182 
1183   NOTES
1184     This mainly updates status variables
1185 */
1186 
end_connection(THD * thd)1187 void end_connection(THD *thd)
1188 {
1189   NET *net= &thd->net;
1190 
1191 #ifdef WITH_WSREP
1192   if (thd->wsrep_cs().state() == wsrep::client_state::s_exec)
1193   {
1194     /* Error happened after the thread acquired ownership to wsrep
1195        client state, but before command was processed. Clean up the
1196        state before wsrep_close(). */
1197     wsrep_after_command_ignore_result(thd);
1198   }
1199   wsrep_close(thd);
1200 #endif /* WITH_WSREP */
1201   if (thd->user_connect)
1202   {
1203     /*
1204       We decrease this variable early to make it easy to log again quickly.
1205       This code is not critical as we will in any case do this test
1206       again in thd->cleanup()
1207     */
1208     decrease_user_connections(thd->user_connect);
1209     /*
1210       The thread may returned back to the pool and assigned to a user
1211       that doesn't have a limit. Ensure the user is not using resources
1212       of someone else.
1213     */
1214     thd->user_connect= NULL;
1215   }
1216 
1217   if (unlikely(thd->killed) || (net->error && net->vio != 0))
1218   {
1219     statistic_increment(aborted_threads,&LOCK_status);
1220     status_var_increment(thd->status_var.lost_connections);
1221   }
1222 
1223   if (likely(!thd->killed) && (net->error && net->vio != 0))
1224     thd->print_aborted_warning(1, thd->get_stmt_da()->is_error()
1225              ? thd->get_stmt_da()->message() : ER_THD(thd, ER_UNKNOWN_ERROR));
1226 }
1227 
1228 
1229 /*
1230   Initialize THD to handle queries
1231 */
1232 
prepare_new_connection_state(THD * thd)1233 void prepare_new_connection_state(THD* thd)
1234 {
1235   Security_context *sctx= thd->security_ctx;
1236 
1237   if (thd->client_capabilities & CLIENT_COMPRESS)
1238     thd->net.compress=1;				// Use compression
1239 
1240   /*
1241     Much of this is duplicated in create_embedded_thd() for the
1242     embedded server library.
1243     TODO: refactor this to avoid code duplication there
1244   */
1245   thd->proc_info= 0;
1246   thd->set_command(COM_SLEEP);
1247   thd->init_for_queries();
1248 
1249   if (opt_init_connect.length && !(sctx->master_access & SUPER_ACL))
1250   {
1251     execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
1252     if (unlikely(thd->is_error()))
1253     {
1254       Host_errors errors;
1255       thd->set_killed(KILL_CONNECTION);
1256       thd->print_aborted_warning(0, "init_connect command failed");
1257       sql_print_warning("%s", thd->get_stmt_da()->message());
1258 
1259       /*
1260         now let client to send its first command,
1261         to be able to send the error back
1262       */
1263       NET *net= &thd->net;
1264       thd->lex->current_select= 0;
1265       my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
1266       thd->clear_error();
1267       net_new_transaction(net);
1268       ulong packet_length= my_net_read(net);
1269       /*
1270         If my_net_read() failed, my_error() has been already called,
1271         and the main Diagnostics Area contains an error condition.
1272       */
1273       if (packet_length != packet_error)
1274         my_error(ER_NEW_ABORTING_CONNECTION, MYF(0),
1275                  thd->thread_id,
1276                  thd->db.str ? thd->db.str : "unconnected",
1277                  sctx->user ? sctx->user : "unauthenticated",
1278                  sctx->host_or_ip, "init_connect command failed");
1279       thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
1280       thd->protocol->end_statement();
1281       thd->killed = KILL_CONNECTION;
1282       errors.m_init_connect= 1;
1283       inc_host_errors(thd->main_security_ctx.ip, &errors);
1284       return;
1285     }
1286 
1287     thd->proc_info=0;
1288     thd->init_for_queries();
1289   }
1290 }
1291 
1292 
1293 /*
1294   Thread handler for a connection
1295 
1296   SYNOPSIS
1297     handle_one_connection()
1298     arg		Connection object (THD)
1299 
1300   IMPLEMENTATION
1301     This function (normally) does the following:
1302     - Initialize thread
1303     - Initialize THD to be used with this thread
1304     - Authenticate user
1305     - Execute all queries sent on the connection
1306     - Take connection down
1307     - End thread  / Handle next connection using thread from thread cache
1308 */
1309 
handle_one_connection(void * arg)1310 pthread_handler_t handle_one_connection(void *arg)
1311 {
1312   CONNECT *connect= (CONNECT*) arg;
1313 
1314   mysql_thread_set_psi_id(connect->thread_id);
1315 
1316   do_handle_one_connection(connect);
1317   return 0;
1318 }
1319 
thd_prepare_connection(THD * thd)1320 bool thd_prepare_connection(THD *thd)
1321 {
1322   bool rc;
1323   lex_start(thd);
1324   rc= login_connection(thd);
1325   if (rc)
1326     return rc;
1327 
1328   MYSQL_CONNECTION_START(thd->thread_id, &thd->security_ctx->priv_user[0],
1329                          (char *) thd->security_ctx->host_or_ip);
1330 
1331   prepare_new_connection_state(thd);
1332 #ifdef WITH_WSREP
1333   thd->wsrep_client_thread= true;
1334   wsrep_open(thd);
1335 #endif /* WITH_WSREP */
1336   return FALSE;
1337 }
1338 
thd_is_connection_alive(THD * thd)1339 bool thd_is_connection_alive(THD *thd)
1340 {
1341   NET *net= &thd->net;
1342   if (likely(!net->error &&
1343              net->vio != 0 &&
1344              thd->killed < KILL_CONNECTION))
1345     return TRUE;
1346   return FALSE;
1347 }
1348 
1349 
do_handle_one_connection(CONNECT * connect)1350 void do_handle_one_connection(CONNECT *connect)
1351 {
1352   ulonglong thr_create_utime= microsecond_interval_timer();
1353   THD *thd;
1354   if (connect->scheduler->init_new_connection_thread() ||
1355       !(thd= connect->create_thd(NULL)))
1356   {
1357     scheduler_functions *scheduler= connect->scheduler;
1358     connect->close_with_error(0, 0, ER_OUT_OF_RESOURCES);
1359     scheduler->end_thread(0, 0);
1360     return;
1361   }
1362 
1363   DBUG_EXECUTE_IF("CONNECT_wait",
1364   {
1365     extern MYSQL_SOCKET unix_sock;
1366     DBUG_ASSERT(unix_sock.fd >= 0);
1367     while (unix_sock.fd >= 0)
1368       my_sleep(1000);
1369   });
1370 
1371   /*
1372     If a thread was created to handle this connection:
1373     increment slow_launch_threads counter if it took more than
1374     slow_launch_time seconds to create the thread.
1375   */
1376 
1377   if (connect->prior_thr_create_utime)
1378   {
1379     ulong launch_time= (ulong) (thr_create_utime -
1380                                 connect->prior_thr_create_utime);
1381     if (launch_time >= slow_launch_time*1000000L)
1382       statistic_increment(slow_launch_threads, &LOCK_status);
1383   }
1384 
1385   server_threads.insert(thd); // Make THD visible in show processlist
1386 
1387   delete connect; // must be after server_threads.insert, see close_connections()
1388 
1389   thd->thr_create_utime= thr_create_utime;
1390   /* We need to set this because of time_out_user_resource_limits */
1391   thd->start_utime= thr_create_utime;
1392 
1393   /*
1394     handle_one_connection() is normally the only way a thread would
1395     start and would always be on the very high end of the stack ,
1396     therefore, the thread stack always starts at the address of the
1397     first local variable of handle_one_connection, which is thd. We
1398     need to know the start of the stack so that we could check for
1399     stack overruns.
1400   */
1401   thd->thread_stack= (char*) &thd;
1402   if (setup_connection_thread_globals(thd))
1403     return;
1404 
1405   for (;;)
1406   {
1407     bool create_user= TRUE;
1408 
1409     mysql_socket_set_thread_owner(thd->net.vio->mysql_socket);
1410     if (thd_prepare_connection(thd))
1411     {
1412       create_user= FALSE;
1413       goto end_thread;
1414     }
1415 
1416     while (thd_is_connection_alive(thd))
1417     {
1418       if (mysql_audit_release_required(thd))
1419         mysql_audit_release(thd);
1420       if (do_command(thd))
1421 	break;
1422     }
1423     end_connection(thd);
1424 
1425 end_thread:
1426     close_connection(thd);
1427 
1428     if (thd->userstat_running)
1429       update_global_user_stats(thd, create_user, time(NULL));
1430 
1431     if (thd->scheduler->end_thread(thd, 1))
1432       return;                                 // Probably no-threads
1433 
1434     /*
1435       If end_thread() returns, this thread has been schedule to
1436       handle the next connection.
1437     */
1438     thd= current_thd;
1439     thd->thread_stack= (char*) &thd;
1440   }
1441 }
1442 #endif /* EMBEDDED_LIBRARY */
1443 
1444 
1445 /* Handling of CONNECT objects */
1446 
1447 /*
1448   Close connection without error and delete the connect object
1449   This and close_with_error are only called if we didn't manage to
1450   create a new thd object.
1451 */
1452 
close_and_delete()1453 void CONNECT::close_and_delete()
1454 {
1455   DBUG_ENTER("close_and_delete");
1456 
1457   if (vio)
1458     vio_close(vio);
1459   if (thread_count_incremented)
1460     dec_connection_count(scheduler);
1461   statistic_increment(connection_errors_internal, &LOCK_status);
1462   statistic_increment(aborted_connects,&LOCK_status);
1463 
1464   delete this;
1465   DBUG_VOID_RETURN;
1466 }
1467 
1468 /*
1469   Close a connection with a possible error to the end user
1470   Alse deletes the connection object, like close_and_delete()
1471 */
1472 
close_with_error(uint sql_errno,const char * message,uint close_error)1473 void CONNECT::close_with_error(uint sql_errno,
1474                                const char *message, uint close_error)
1475 {
1476   THD *thd= create_thd(NULL);
1477   if (thd)
1478   {
1479     if (sql_errno)
1480       net_send_error(thd, sql_errno, message, NULL);
1481     close_connection(thd, close_error);
1482     delete thd;
1483     set_current_thd(0);
1484   }
1485   close_and_delete();
1486 }
1487 
1488 
~CONNECT()1489 CONNECT::~CONNECT()
1490 {
1491   if (vio)
1492     vio_delete(vio);
1493   count--;
1494 }
1495 
1496 
1497 /* Reuse or create a THD based on a CONNECT object */
1498 
create_thd(THD * thd)1499 THD *CONNECT::create_thd(THD *thd)
1500 {
1501   bool res, thd_reused= thd != 0;
1502   DBUG_ENTER("create_thd");
1503 
1504   DBUG_EXECUTE_IF("simulate_failed_connection_2", DBUG_RETURN(0); );
1505 
1506   if (thd)
1507   {
1508     /* reuse old thd */
1509     thd->reset_for_reuse();
1510     /*
1511       reset tread_id's, but not thread_dbug_id's as the later isn't allowed
1512       to change as there is already structures in thd marked with the old
1513       value.
1514     */
1515     thd->thread_id= thd->variables.pseudo_thread_id= thread_id;
1516   }
1517   else if (!(thd= new THD(thread_id)))
1518     DBUG_RETURN(0);
1519 
1520   set_current_thd(thd);
1521   res= my_net_init(&thd->net, vio, thd, MYF(MY_THREAD_SPECIFIC));
1522   vio= 0;                              // Vio now handled by thd
1523 
1524   if (unlikely(res || thd->is_error()))
1525   {
1526     if (!thd_reused)
1527       delete thd;
1528     set_current_thd(0);
1529     DBUG_RETURN(0);
1530   }
1531 
1532   init_net_server_extension(thd);
1533 
1534   thd->security_ctx->host= host;
1535   thd->extra_port=         extra_port;
1536   thd->scheduler=          scheduler;
1537   thd->real_id=            real_id;
1538   DBUG_RETURN(thd);
1539 }
1540