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