1 /*
2    Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include "sql/conn_handler/socket_connection.h"
26 
27 #include "my_config.h"
28 
29 #include <errno.h>
30 #include <fcntl.h>
31 
32 #include <limits.h>
33 #ifndef _WIN32
34 #include <netdb.h>
35 #endif
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
38 #endif
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
46 #include <sys/stat.h>
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #include <algorithm>
51 #include <atomic>
52 #include <memory>  // std::unique_ptr
53 #include <new>
54 #include <utility>
55 
56 #include "m_string.h"
57 #include "my_dbug.h"
58 #include "my_io.h"
59 #include "my_loglevel.h"
60 #include "my_sys.h"
61 #include "my_thread.h"
62 #include "mysql/components/services/log_builtins.h"
63 #include "mysql/psi/mysql_thread.h"
64 #include "mysqld_error.h"
65 #include "sql-common/net_ns.h"
66 #include "sql/auth/sql_security_ctx.h"
67 #include "sql/conn_handler/channel_info.h"               // Channel_info
68 #include "sql/conn_handler/init_net_server_extension.h"  // init_net_server_extension
69 #include "sql/log.h"
70 #include "sql/mysqld.h"     // key_socket_tcpip
71 #include "sql/sql_class.h"  // THD
72 #include "sql/sql_const.h"
73 #include "violite.h"  // Vio
74 #ifdef HAVE_SYS_UN_H
75 #include <sys/un.h>
76 #endif
77 #ifdef HAVE_LIBWRAP
78 #include <syslog.h>
79 #ifndef HAVE_LIBWRAP_PROTOTYPES
80 extern "C" {
81 #include <tcpd.h>
82 }
83 #else
84 #include <tcpd.h>
85 #endif
86 #endif
87 #include "connection_handler_manager.h"
88 
89 using std::max;
90 
91 /** Number of connection errors when selecting on the listening port */
92 static std::atomic<ulong> connection_errors_select{0};
93 
94 /** Number of connection errors when accepting sockets in the listening port. */
95 static std::atomic<ulong> connection_errors_accept{0};
96 
97 /** Number of connection errors from TCP wrappers. */
98 static std::atomic<ulong> connection_errors_tcpwrap{0};
99 
100 namespace {
101 struct FreeAddrInfoDeleter {
operator ()__anon2d3303710111::FreeAddrInfoDeleter102   void operator()(addrinfo *ai) {
103     if (ai != nullptr) {
104       freeaddrinfo(ai);
105     }
106   }
107 };
108 
109 using AddrInfoPtr = std::unique_ptr<addrinfo, FreeAddrInfoDeleter>;
GetAddrInfoPtr(const char * node,const char * service,const addrinfo * hints)110 AddrInfoPtr GetAddrInfoPtr(const char *node, const char *service,
111                            const addrinfo *hints) {
112   addrinfo *p = nullptr;
113   int err = getaddrinfo(node, service, hints, &p);
114   AddrInfoPtr nrv{p};
115   return (err == 0 ? std::move(nrv) : nullptr);
116 }
117 }  // namespace
118 
get_connection_errors_select()119 ulong get_connection_errors_select() { return connection_errors_select.load(); }
120 
get_connection_errors_accept()121 ulong get_connection_errors_accept() { return connection_errors_accept.load(); }
122 
get_connection_errors_tcpwrap()123 ulong get_connection_errors_tcpwrap() {
124   return connection_errors_tcpwrap.load();
125 }
126 
127 #ifdef HAVE_LIBWRAP
128 static const char *libwrap_name;
129 #endif
130 
131 ///////////////////////////////////////////////////////////////////////////
132 // Channel_info_local_socket implementation
133 ///////////////////////////////////////////////////////////////////////////
134 
135 /**
136   This class abstracts the info. about local socket mode of communication with
137   the server.
138 */
139 class Channel_info_local_socket : public Channel_info {
140   // connect socket object
141   MYSQL_SOCKET m_connect_sock;
142 
143  protected:
create_and_init_vio() const144   virtual Vio *create_and_init_vio() const {
145     Vio *vio =
146         mysql_socket_vio_new(m_connect_sock, VIO_TYPE_SOCKET, VIO_LOCALHOST);
147 #ifdef USE_PPOLL_IN_VIO
148     if (vio != nullptr) {
149       vio->thread_id = my_thread_self();
150       vio->signal_mask = mysqld_signal_mask;
151     }
152 #endif
153     return vio;
154   }
155 
156  public:
157   /**
158     Constructor that sets the connect socket.
159 
160     @param connect_socket set connect socket descriptor.
161   */
Channel_info_local_socket(MYSQL_SOCKET connect_socket)162   Channel_info_local_socket(MYSQL_SOCKET connect_socket)
163       : m_connect_sock(connect_socket) {}
164 
create_thd()165   virtual THD *create_thd() {
166     THD *thd = Channel_info::create_thd();
167 
168     if (thd != nullptr) {
169       init_net_server_extension(thd);
170       thd->security_context()->set_host_ptr(my_localhost, strlen(my_localhost));
171     }
172     return thd;
173   }
174 
send_error_and_close_channel(uint errorcode,int error,bool senderror)175   virtual void send_error_and_close_channel(uint errorcode, int error,
176                                             bool senderror) {
177     Channel_info::send_error_and_close_channel(errorcode, error, senderror);
178 
179     mysql_socket_shutdown(m_connect_sock, SHUT_RDWR);
180     mysql_socket_close(m_connect_sock);
181   }
182 };
183 
184 ///////////////////////////////////////////////////////////////////////////
185 // Channel_info_tcpip_socket implementation
186 ///////////////////////////////////////////////////////////////////////////
187 
188 /**
189   This class abstracts the info. about TCP/IP socket mode of communication with
190   the server.
191 */
192 class Channel_info_tcpip_socket : public Channel_info {
193   // connect socket object
194   MYSQL_SOCKET m_connect_sock;
195   /*
196     Flag specifying whether a connection is admin connection or
197     ordinary connection.
198   */
199   bool m_is_admin_conn;
200 #ifdef HAVE_SETNS
201   /*
202     Network namespace associated with the socket.
203   */
204   std::string m_network_namespace;
205 #endif
206 
207  protected:
create_and_init_vio() const208   virtual Vio *create_and_init_vio() const {
209     Vio *vio = mysql_socket_vio_new(m_connect_sock, VIO_TYPE_TCPIP, 0);
210 #ifdef USE_PPOLL_IN_VIO
211     if (vio != nullptr) {
212       vio->thread_id = my_thread_self();
213       vio->signal_mask = mysqld_signal_mask;
214     }
215 #endif
216 
217 #ifdef HAVE_SETNS
218     strncpy(vio->network_namespace, m_network_namespace.c_str(),
219             sizeof(vio->network_namespace) - 1);
220     vio->network_namespace[sizeof(vio->network_namespace) - 1] = '\0';
221 #endif
222 
223     return vio;
224   }
225 
226  public:
227   /**
228     Constructor that sets the connect socket.
229 
230     @param connect_socket set connect socket descriptor.
231     @param is_admin_conn  flag specifying whether a connection is admin
232                           connection.
233   */
Channel_info_tcpip_socket(MYSQL_SOCKET connect_socket,bool is_admin_conn)234   Channel_info_tcpip_socket(MYSQL_SOCKET connect_socket, bool is_admin_conn)
235       : m_connect_sock(connect_socket), m_is_admin_conn(is_admin_conn) {}
236 
create_thd()237   virtual THD *create_thd() {
238     THD *thd = Channel_info::create_thd();
239 
240     if (thd != nullptr) {
241       thd->set_admin_connection(m_is_admin_conn);
242       init_net_server_extension(thd);
243     }
244     return thd;
245   }
246 
send_error_and_close_channel(uint errorcode,int error,bool senderror)247   virtual void send_error_and_close_channel(uint errorcode, int error,
248                                             bool senderror) {
249     Channel_info::send_error_and_close_channel(errorcode, error, senderror);
250 
251     mysql_socket_shutdown(m_connect_sock, SHUT_RDWR);
252     mysql_socket_close(m_connect_sock);
253   }
254 
is_admin_connection() const255   virtual bool is_admin_connection() const { return m_is_admin_conn; }
256 
257 #ifdef HAVE_SETNS
258   /**
259     Set a network namespace for channel.
260 
261     @param network_namespace  Network namespace associated with a channel.
262   */
set_network_namespace(const std::string & network_namespace)263   void set_network_namespace(const std::string &network_namespace) {
264     m_network_namespace = network_namespace;
265   }
266 #endif
267 };
268 
269 ///////////////////////////////////////////////////////////////////////////
270 // TCP_socket implementation
271 ///////////////////////////////////////////////////////////////////////////
272 #ifdef _WIN32
273 using Socket_error_message_buf = TCHAR[1024];
274 #endif
275 
276 /**
277   MY_BIND_ALL_ADDRESSES defines a special value for the bind-address option,
278   which means that the server should listen to all available network addresses,
279   both IPv6 (if available) and IPv4.
280 
281   Basically, this value instructs the server to make an attempt to bind the
282   server socket to '::' address, and rollback to '0.0.0.0' if the attempt fails.
283 */
284 const char *MY_BIND_ALL_ADDRESSES = "*";
285 
286 const char *ipv4_all_addresses = "0.0.0.0";
287 
288 const char *ipv6_all_addresses = "::";
289 
290 /**
291   TCP_socket class represents the TCP sockets abstraction. It provides
292   the get_listener_socket that setup a TCP listener socket to listen.
293 */
294 class TCP_socket {
295   std::string m_bind_addr_str;      // IP address as string.
296   std::string m_network_namespace;  // Network namespace if specified
297   uint m_tcp_port;                  // TCP port to bind to
298   uint m_backlog;       // Backlog length for queue of pending connections.
299   uint m_port_timeout;  // Port timeout
300 
create_socket(const struct addrinfo * addrinfo_list,int addr_family,struct addrinfo ** use_addrinfo)301   MYSQL_SOCKET create_socket(const struct addrinfo *addrinfo_list,
302                              int addr_family, struct addrinfo **use_addrinfo) {
303     for (const struct addrinfo *cur_ai = addrinfo_list; cur_ai != nullptr;
304          cur_ai = cur_ai->ai_next) {
305       if (cur_ai->ai_family != addr_family) continue;
306 
307       MYSQL_SOCKET sock =
308           mysql_socket_socket(key_socket_tcpip, cur_ai->ai_family,
309                               cur_ai->ai_socktype, cur_ai->ai_protocol);
310 
311       char ip_addr[INET6_ADDRSTRLEN];
312 
313       if (vio_getnameinfo(cur_ai->ai_addr, ip_addr, sizeof(ip_addr), nullptr, 0,
314                           NI_NUMERICHOST)) {
315         ip_addr[0] = 0;
316       }
317 
318       if (mysql_socket_getfd(sock) == INVALID_SOCKET) {
319         LogErr(ERROR_LEVEL, ER_CONN_TCP_NO_SOCKET,
320                (addr_family == AF_INET) ? "IPv4" : "IPv6",
321                (const char *)ip_addr, (int)socket_errno);
322       } else {
323         LogErr(INFORMATION_LEVEL, ER_CONN_TCP_CREATED, (const char *)ip_addr);
324 
325         *use_addrinfo = const_cast<addrinfo *>(cur_ai);
326         return sock;
327       }
328     }
329 
330     return MYSQL_INVALID_SOCKET;
331   }
332 
333  public:
334   /**
335     Constructor that takes tcp port and ip address string and other
336     related parameters to set up listener tcp to listen for connection
337     events.
338 
339     @param  bind_addr_str  ip address as string value.
340     @param  network_namespace_str  network namespace as string value
341     @param  tcp_port  tcp port number.
342     @param  backlog backlog specifying length of pending connection queue.
343     @param  port_timeout port timeout value
344   */
TCP_socket(std::string bind_addr_str,std::string network_namespace_str,uint tcp_port,uint backlog,uint port_timeout)345   TCP_socket(std::string bind_addr_str, std::string network_namespace_str,
346              uint tcp_port, uint backlog, uint port_timeout)
347       : m_bind_addr_str(bind_addr_str),
348         m_network_namespace(network_namespace_str),
349         m_tcp_port(tcp_port),
350         m_backlog(backlog),
351         m_port_timeout(port_timeout) {}
352 
353   /**
354     Set up a listener to listen for connection events.
355 
356     @retval   valid socket if successful else MYSQL_INVALID_SOCKET on failure.
357   */
get_listener_socket()358   MYSQL_SOCKET get_listener_socket() {
359     const char *bind_address_str = nullptr;
360 
361     LogErr(INFORMATION_LEVEL, ER_CONN_TCP_ADDRESS, m_bind_addr_str.c_str(),
362            m_tcp_port);
363 
364     // Get list of IP-addresses associated with the bind-address.
365 
366     struct addrinfo hints;
367     memset(&hints, 0, sizeof(hints));
368     hints.ai_flags = AI_PASSIVE;
369     hints.ai_socktype = SOCK_STREAM;
370     hints.ai_family = AF_UNSPEC;
371 
372     char port_buf[NI_MAXSERV];
373     snprintf(port_buf, NI_MAXSERV, "%d", m_tcp_port);
374 
375     if (!m_network_namespace.empty()) {
376 #ifdef HAVE_SETNS
377       if (set_network_namespace(m_network_namespace))
378         return MYSQL_INVALID_SOCKET;
379 #else
380       LogErr(ERROR_LEVEL, ER_NETWORK_NAMESPACES_NOT_SUPPORTED);
381       return MYSQL_INVALID_SOCKET;
382 #endif
383     }
384 
385     // Create a RAII guard for addrinfo struct.
386     AddrInfoPtr ai_ptr{nullptr};
387 
388     if (native_strcasecmp(m_bind_addr_str.c_str(), MY_BIND_ALL_ADDRESSES) ==
389         0) {
390       /*
391         That's the case when bind-address is set to a special value ('*'),
392         meaning "bind to all available IP addresses". If the box supports
393         the IPv6 stack, that means binding to '::'. If only IPv4 is available,
394         bind to '0.0.0.0'.
395       */
396 
397       bool ipv6_available = false;
398       ai_ptr = GetAddrInfoPtr(ipv6_all_addresses, port_buf, &hints);
399       if (ai_ptr) {
400         /*
401           IPv6 might be available (the system might be able to resolve an IPv6
402           address, but not be able to create an IPv6-socket). Try to create a
403           dummy IPv6-socket. Do not instrument that socket by P_S.
404         */
405 
406         MYSQL_SOCKET s = mysql_socket_socket(0, AF_INET6, SOCK_STREAM, 0);
407         ipv6_available = mysql_socket_getfd(s) != INVALID_SOCKET;
408         if (ipv6_available) mysql_socket_close(s);
409       }
410       if (ipv6_available &&
411           DBUG_EVALUATE_IF("sim_ipv6_unavailable", false, true)) {
412         LogErr(INFORMATION_LEVEL, ER_CONN_TCP_IPV6_AVAILABLE);
413 
414         // Address info (ai) for IPv6 address is already set.
415 
416         bind_address_str = ipv6_all_addresses;
417       } else {
418         LogErr(INFORMATION_LEVEL, ER_CONN_TCP_IPV6_UNAVAILABLE);
419 
420         // Retrieve address info (ai) for IPv4 address.
421         ai_ptr = GetAddrInfoPtr(ipv4_all_addresses, port_buf, &hints);
422         if (!ai_ptr) {
423 #ifdef _WIN32
424           Socket_error_message_buf msg_buff;
425           FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, socket_errno,
426                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
427                         (LPTSTR)msg_buff, sizeof(msg_buff), NULL);
428           LogErr(ERROR_LEVEL, ER_CONN_TCP_ERROR_WITH_STRERROR, msg_buff);
429 #else
430           LogErr(ERROR_LEVEL, ER_CONN_TCP_ERROR_WITH_STRERROR, strerror(errno));
431 #endif
432           LogErr(ERROR_LEVEL, ER_CONN_TCP_CANT_RESOLVE_HOSTNAME);
433 #ifdef HAVE_SETNS
434           if (!m_network_namespace.empty())
435             (void)restore_original_network_namespace();
436 #endif
437           return MYSQL_INVALID_SOCKET;
438         }  // !ai_ptr
439         bind_address_str = ipv4_all_addresses;
440       }
441     } else {
442       ai_ptr = GetAddrInfoPtr(m_bind_addr_str.c_str(), port_buf, &hints);
443       if (!ai_ptr) {
444 #ifdef _WIN32
445         Socket_error_message_buf msg_buff;
446         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, socket_errno,
447                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
448                       (LPTSTR)msg_buff, sizeof(msg_buff), NULL);
449         LogErr(ERROR_LEVEL, ER_CONN_TCP_ERROR_WITH_STRERROR, msg_buff);
450 #else
451         LogErr(ERROR_LEVEL, ER_CONN_TCP_ERROR_WITH_STRERROR, strerror(errno));
452 #endif
453         LogErr(ERROR_LEVEL, ER_CONN_TCP_CANT_RESOLVE_HOSTNAME);
454 #ifdef HAVE_SETNS
455         if (!m_network_namespace.empty())
456           (void)restore_original_network_namespace();
457 #endif
458 
459         return MYSQL_INVALID_SOCKET;
460       }  // !ai_ptr
461       bind_address_str = m_bind_addr_str.c_str();
462     }
463 
464     // Log all the IP-addresses
465     for (struct addrinfo *cur_ai = ai_ptr.get(); cur_ai != nullptr;
466          cur_ai = cur_ai->ai_next) {
467       char ip_addr[INET6_ADDRSTRLEN];
468 
469       if (vio_getnameinfo(cur_ai->ai_addr, ip_addr, sizeof(ip_addr), nullptr, 0,
470                           NI_NUMERICHOST)) {
471         LogErr(ERROR_LEVEL, ER_CONN_TCP_IP_NOT_LOGGED);
472         continue;
473       }
474 
475       LogErr(INFORMATION_LEVEL, ER_CONN_TCP_RESOLVE_INFO, bind_address_str,
476              ip_addr);
477     }
478 
479     /*
480       If the 'bind-address' option specifies the hostname, which resolves to
481       multiple IP-address, use the following rule:
482       - if there are IPv4-addresses, use the first IPv4-address
483       returned by getaddrinfo();
484       - if there are IPv6-addresses, use the first IPv6-address
485       returned by getaddrinfo();
486     */
487 
488     struct addrinfo *a = nullptr;
489 
490     MYSQL_SOCKET listener_socket = create_socket(ai_ptr.get(), AF_INET, &a);
491 
492     if (mysql_socket_getfd(listener_socket) == INVALID_SOCKET)
493       listener_socket = create_socket(ai_ptr.get(), AF_INET6, &a);
494 
495 #ifdef HAVE_SETNS
496     if (!m_network_namespace.empty() && restore_original_network_namespace())
497       return MYSQL_INVALID_SOCKET;
498 #endif
499 
500     // Report user-error if we failed to create a socket.
501     if (mysql_socket_getfd(listener_socket) == INVALID_SOCKET) {
502 #ifdef _WIN32
503       Socket_error_message_buf msg_buff;
504       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, socket_errno,
505                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)msg_buff,
506                     sizeof(msg_buff), NULL);
507       LogErr(ERROR_LEVEL, ER_CONN_TCP_ERROR_WITH_STRERROR, msg_buff);
508 #else
509       LogErr(ERROR_LEVEL, ER_CONN_TCP_ERROR_WITH_STRERROR, strerror(errno));
510 #endif
511       return MYSQL_INVALID_SOCKET;
512     }
513 
514     mysql_socket_set_thread_owner(listener_socket);
515 
516 #ifndef _WIN32
517     /*
518       We should not use SO_REUSEADDR on windows as this would enable a
519       user to open two mysqld servers with the same TCP/IP port.
520     */
521     {
522       int option_flag = 1;
523       (void)mysql_socket_setsockopt(listener_socket, SOL_SOCKET, SO_REUSEADDR,
524                                     (char *)&option_flag, sizeof(option_flag));
525     }
526 #endif
527 #ifdef IPV6_V6ONLY
528     /*
529       For interoperability with older clients, IPv6 socket should
530       listen on both IPv6 and IPv4 wildcard addresses.
531       Turn off IPV6_V6ONLY option.
532 
533       NOTE: this will work starting from Windows Vista only.
534       On Windows XP dual stack is not available, so it will not
535       listen on the corresponding IPv4-address.
536     */
537     if (a->ai_family == AF_INET6) {
538       int option_flag = 0;
539 
540       if (mysql_socket_setsockopt(listener_socket, IPPROTO_IPV6, IPV6_V6ONLY,
541                                   (char *)&option_flag, sizeof(option_flag))) {
542         LogErr(WARNING_LEVEL, ER_CONN_TCP_CANT_RESET_V6ONLY, (int)socket_errno);
543       }
544     }
545 #endif
546     /*
547       Sometimes the port is not released fast enough when stopping and
548       restarting the server. This happens quite often with the test suite
549       on busy Linux systems. Retry to bind the address at these intervals:
550       Sleep intervals: 1, 2, 4,  6,  9, 13, 17, 22, ...
551       Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ...
552       Limit the sequence by m_port_timeout (set --port-open-timeout=#).
553     */
554     uint this_wait = 0;
555     int ret = 0;
556     for (uint waited = 0, retry = 1;; retry++, waited += this_wait) {
557       if (((ret = mysql_socket_bind(listener_socket, a->ai_addr,
558                                     a->ai_addrlen)) >= 0) ||
559           (socket_errno != SOCKET_EADDRINUSE) || (waited >= m_port_timeout))
560         break;
561       LogErr(INFORMATION_LEVEL, ER_CONN_TCP_BIND_RETRY, mysqld_port);
562       this_wait = retry * retry / 3 + 1;
563       sleep(this_wait);
564     }
565 
566     if (ret < 0) {
567       DBUG_PRINT("error", ("Got error: %d from bind", socket_errno));
568 #ifdef _WIN32
569       Socket_error_message_buf msg_buff;
570       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, socket_errno,
571                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)msg_buff,
572                     sizeof(msg_buff), NULL);
573       LogErr(ERROR_LEVEL, ER_CONN_TCP_BIND_FAIL, msg_buff);
574 #else
575       LogErr(ERROR_LEVEL, ER_CONN_TCP_BIND_FAIL, strerror(socket_errno));
576 #endif
577       LogErr(ERROR_LEVEL, ER_CONN_TCP_IS_THERE_ANOTHER_USING_PORT, m_tcp_port);
578       mysql_socket_close(listener_socket);
579       return MYSQL_INVALID_SOCKET;
580     }
581 
582     if (mysql_socket_listen(listener_socket, static_cast<int>(m_backlog)) < 0) {
583 #ifdef _WIN32
584       Socket_error_message_buf msg_buff;
585       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, socket_errno,
586                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)msg_buff,
587                     sizeof(msg_buff), NULL);
588       LogErr(ERROR_LEVEL, ER_CONN_TCP_START_FAIL, msg_buff);
589 #else
590       LogErr(ERROR_LEVEL, ER_CONN_TCP_START_FAIL, strerror(errno));
591 #endif
592 
593       LogErr(ERROR_LEVEL, ER_CONN_TCP_LISTEN_FAIL, socket_errno);
594       mysql_socket_close(listener_socket);
595       return MYSQL_INVALID_SOCKET;
596     }
597 
598 #if !defined(NO_FCNTL_NONBLOCK)
599     (void)mysql_sock_set_nonblocking(listener_socket);
600 #endif
601 
602     return listener_socket;
603   }
604 };
605 
606 #if defined(HAVE_SYS_UN_H)
607 ///////////////////////////////////////////////////////////////////////////
608 // Unix_socket implementation
609 ///////////////////////////////////////////////////////////////////////////
610 
611 /**
612   The Unix_socket class represents an abstraction for creating a unix
613   socket ready to listen for new connections from clients.
614 */
615 class Unix_socket {
616   std::string m_unix_sockname;  // pathname for socket to bind to.
617   uint m_backlog;  // backlog specifying lenght of pending queue connection.
618   /**
619     Create a lockfile which contains the pid of the mysqld instance started
620     and pathname as name of unix socket pathname appended with .lock
621 
622     @retval   false if lockfile creation is successful else true if lockfile
623               file could not be created.
624 
625   */
626   bool create_lockfile();
627 
628  public:
629   /**
630     Constructor that takes pathname for unix socket to bind to
631     and backlog specifying the length of pending connection queue.
632 
633     @param  unix_sockname pointer to pathname for the created unix socket
634             to bind.
635     @param  backlog   specifying the length of pending connection queue.
636   */
Unix_socket(const std::string * unix_sockname,uint backlog)637   Unix_socket(const std::string *unix_sockname, uint backlog)
638       : m_unix_sockname(*unix_sockname), m_backlog(backlog) {}
639 
640   /**
641     Set up a listener socket which is ready to listen for connection from
642     clients.
643 
644     @retval   valid socket if successful else MYSQL_INVALID_SOCKET on failure.
645   */
get_listener_socket()646   MYSQL_SOCKET get_listener_socket() {
647     struct sockaddr_un UNIXaddr;
648     DBUG_PRINT("general", ("UNIX Socket is %s", m_unix_sockname.c_str()));
649 
650     // Check path length, probably move to set unix port?
651     if (m_unix_sockname.length() > (sizeof(UNIXaddr.sun_path) - 1)) {
652       LogErr(ERROR_LEVEL, ER_CONN_UNIX_PATH_TOO_LONG,
653              (uint)sizeof(UNIXaddr.sun_path) - 1, m_unix_sockname.c_str());
654       return MYSQL_INVALID_SOCKET;
655     }
656 
657     if (create_lockfile()) {
658       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_FAIL);
659       return MYSQL_INVALID_SOCKET;
660     }
661 
662     MYSQL_SOCKET listener_socket =
663         mysql_socket_socket(key_socket_unix, AF_UNIX, SOCK_STREAM, 0);
664 
665     if (mysql_socket_getfd(listener_socket) < 0) {
666       LogErr(ERROR_LEVEL, ER_CONN_UNIX_NO_FD, strerror(errno));
667       return MYSQL_INVALID_SOCKET;
668     }
669 
670     mysql_socket_set_thread_owner(listener_socket);
671 
672     memset(&UNIXaddr, 0, sizeof(UNIXaddr));
673     UNIXaddr.sun_family = AF_UNIX;
674     my_stpcpy(UNIXaddr.sun_path, m_unix_sockname.c_str());
675     (void)unlink(m_unix_sockname.c_str());
676 
677     // Set socket option SO_REUSEADDR
678     int option_enable = 1;
679     (void)mysql_socket_setsockopt(listener_socket, SOL_SOCKET, SO_REUSEADDR,
680                                   (char *)&option_enable,
681                                   sizeof(option_enable));
682     // bind
683     umask(0);
684     if (mysql_socket_bind(listener_socket,
685                           reinterpret_cast<struct sockaddr *>(&UNIXaddr),
686                           sizeof(UNIXaddr)) < 0) {
687       LogErr(ERROR_LEVEL, ER_CONN_UNIX_NO_BIND_NO_START, strerror(errno));
688       LogErr(ERROR_LEVEL, ER_CONN_UNIX_IS_THERE_ANOTHER_USING_SOCKET,
689              m_unix_sockname.c_str());
690       mysql_socket_close(listener_socket);
691       return MYSQL_INVALID_SOCKET;
692     }
693     umask(((~my_umask) & 0666));
694 
695     // listen
696     if (mysql_socket_listen(listener_socket, (int)m_backlog) < 0)
697       LogErr(WARNING_LEVEL, ER_CONN_UNIX_LISTEN_FAILED, socket_errno);
698 
699       // set sock fd non blocking.
700 #if !defined(NO_FCNTL_NONBLOCK)
701     (void)mysql_sock_set_nonblocking(listener_socket);
702 #endif
703 
704     return listener_socket;
705   }
706 };
707 
create_lockfile()708 bool Unix_socket::create_lockfile() {
709   int fd;
710   char buffer[8];
711   pid_t cur_pid = getpid();
712   std::string lock_filename = m_unix_sockname + ".lock";
713 
714   static_assert(sizeof(pid_t) == 4, "");
715   int retries = 3;
716   while (true) {
717     if (!retries--) {
718       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_GIVING_UP,
719              lock_filename.c_str());
720       return true;
721     }
722 
723     fd = open(lock_filename.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
724 
725     if (fd >= 0) break;
726 
727     if (errno != EEXIST) {
728       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_CREATE,
729              lock_filename.c_str());
730       return true;
731     }
732 
733     fd = open(lock_filename.c_str(), O_RDONLY, 0600);
734     if (fd < 0) {
735       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_OPEN,
736              lock_filename.c_str());
737       return true;
738     }
739 
740     ssize_t len;
741     if ((len = read(fd, buffer, sizeof(buffer) - 1)) < 0) {
742       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_READ,
743              lock_filename.c_str());
744       close(fd);
745       return true;
746     }
747 
748     close(fd);
749 
750     if (len == 0) {
751       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_EMPTY, lock_filename.c_str());
752       return true;
753     }
754     buffer[len] = '\0';
755 
756     pid_t parent_pid = getppid();
757     pid_t read_pid = atoi(buffer);
758 
759     if (read_pid <= 0) {
760       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_PIDLESS,
761              lock_filename.c_str());
762       return true;
763     }
764 
765     if (read_pid != cur_pid && read_pid != parent_pid) {
766       if (kill(read_pid, 0) == 0) {
767         LogErr(ERROR_LEVEL, ER_CONN_UNIX_PID_CLAIMED_SOCKET_FILE,
768                static_cast<int>(read_pid));
769         return true;
770       }
771     }
772 
773     /*
774       Unlink the lock file as it is not associated with any process and
775       retry.
776     */
777     if (unlink(lock_filename.c_str()) < 0) {
778       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_DELETE,
779              lock_filename.c_str(), errno);
780       return true;
781     }
782   }
783 
784   snprintf(buffer, sizeof(buffer), "%d\n", static_cast<int>(cur_pid));
785   if (write(fd, buffer, strlen(buffer)) !=
786       static_cast<signed>(strlen(buffer))) {
787     close(fd);
788     LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_WRITE,
789            lock_filename.c_str(), errno);
790 
791     if (unlink(lock_filename.c_str()) == -1)
792       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_DELETE,
793              lock_filename.c_str(), errno);
794 
795     return true;
796   }
797 
798   if (fsync(fd) != 0) {
799     close(fd);
800     LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_SYNC, lock_filename.c_str(),
801            errno);
802 
803     if (unlink(lock_filename.c_str()) == -1)
804       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_DELETE,
805              lock_filename.c_str(), errno);
806 
807     return true;
808   }
809 
810   if (close(fd) != 0) {
811     LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_CLOSE,
812            lock_filename.c_str(), errno);
813 
814     if (unlink(lock_filename.c_str()) == -1)
815       LogErr(ERROR_LEVEL, ER_CONN_UNIX_LOCK_FILE_CANT_DELETE,
816              lock_filename.c_str(), errno);
817 
818     return true;
819   }
820   return false;
821 }
822 
823 #endif  // HAVE_SYS_UN_H
824 
825 ///////////////////////////////////////////////////////////////////////////
826 // Mysqld_socket_listener implementation
827 ///////////////////////////////////////////////////////////////////////////
828 
Mysqld_socket_listener(const std::list<Bind_address_info> & bind_addresses,uint tcp_port,const Bind_address_info & admin_bind_addr,uint admin_tcp_port,bool use_separate_thread_for_admin,uint backlog,uint port_timeout,std::string unix_sockname)829 Mysqld_socket_listener::Mysqld_socket_listener(
830     const std::list<Bind_address_info> &bind_addresses, uint tcp_port,
831     const Bind_address_info &admin_bind_addr, uint admin_tcp_port,
832     bool use_separate_thread_for_admin, uint backlog, uint port_timeout,
833     std::string unix_sockname)
834     : m_bind_addresses(bind_addresses),
835       m_admin_bind_address(admin_bind_addr),
836       m_tcp_port(tcp_port),
837       m_admin_tcp_port(admin_tcp_port),
838       m_use_separate_thread_for_admin(use_separate_thread_for_admin),
839       m_backlog(backlog),
840       m_port_timeout(port_timeout),
841       m_unix_sockname(unix_sockname),
842       m_unlink_sockname(false),
843       m_admin_interface_listen_socket(mysql_socket_invalid()) {
844 #ifdef HAVE_LIBWRAP
845   /*
846     Set up syslog parameters on behalf of the TCP-wrappers.
847     The loadable component that logs server errors to syslog
848     may re-open it with user-defined attributes (logging of
849     PIDS / ident) later, but we establish a sensible baseline
850     here in case that log-sink is not used. Note that the
851     wrapper is hard-coded to use LOG_AUTH in the syslog()
852     call below, which lets the wrapper log to a different
853     facility than the rest of the server (the facility of
854     which defaults to LOG_DAEMON and is user-configurable)
855     if desired.
856   */
857   libwrap_name = my_progname + dirname_length(my_progname);
858   openlog(libwrap_name, LOG_PID, LOG_AUTH);
859 #endif /* HAVE_LIBWRAP */
860 }
861 
add_socket_to_listener(MYSQL_SOCKET listen_socket)862 void Mysqld_socket_listener::add_socket_to_listener(
863     MYSQL_SOCKET listen_socket) {
864   mysql_socket_set_thread_owner(listen_socket);
865 
866 #ifdef HAVE_POLL
867   m_poll_info.m_fds.emplace_back(
868       pollfd{mysql_socket_getfd(listen_socket), POLLIN, 0});
869   m_poll_info.m_pfs_fds.push_back(listen_socket);
870 #else   // HAVE_POLL
871   FD_SET(mysql_socket_getfd(listen_socket), &m_select_info.m_client_fds);
872   if ((uint)mysql_socket_getfd(listen_socket) >
873       m_select_info.m_max_used_connection)
874     m_select_info.m_max_used_connection = mysql_socket_getfd(listen_socket);
875 #endif  // HAVE_POLL
876 }
877 
setup_connection_events(const socket_vector_t & socket_vector)878 void Mysqld_socket_listener::setup_connection_events(
879     const socket_vector_t &socket_vector) {
880 #ifdef HAVE_POLL
881   const socket_vector_t::size_type total_number_of_addresses_to_bind =
882       socket_vector.size();
883   m_poll_info.m_fds.reserve(total_number_of_addresses_to_bind);
884   m_poll_info.m_pfs_fds.reserve(total_number_of_addresses_to_bind);
885 #endif
886 
887   for (const auto &socket_element : socket_vector)
888     add_socket_to_listener(socket_element.m_socket);
889 }
890 
891 /**
892   Accept a new connection on a ready listening socket.
893 
894   @param listen_sock  Listening socket ready to accept a new connection
895   @param [out] connect_sock  Socket corresponding to a new accepted connection
896 
897   @return operation result
898     @retval true on error
899     @retval false on success
900 */
accept_connection(MYSQL_SOCKET listen_sock,MYSQL_SOCKET * connect_sock)901 static bool accept_connection(MYSQL_SOCKET listen_sock,
902                               MYSQL_SOCKET *connect_sock) {
903   struct sockaddr_storage c_addr;
904   for (uint retry = 0; retry < MAX_ACCEPT_RETRY; retry++) {
905     socket_len_t length = sizeof(struct sockaddr_storage);
906     *connect_sock =
907         mysql_socket_accept(key_socket_client_connection, listen_sock,
908                             (struct sockaddr *)(&c_addr), &length);
909     if (mysql_socket_getfd(*connect_sock) != INVALID_SOCKET ||
910         (socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
911       break;
912   }
913   if (mysql_socket_getfd(*connect_sock) == INVALID_SOCKET) {
914     /*
915       accept(2) failed on the listening port, after many retries.
916       There is not much details to report about the client,
917       increment the server global status variable.
918     */
919 
920     if ((connection_errors_accept++ & 255) == 0) {  // This can happen often
921 #ifdef _WIN32
922       Socket_error_message_buf msg_buff;
923       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, socket_errno,
924                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)msg_buff,
925                     sizeof(msg_buff), NULL);
926       LogErr(ERROR_LEVEL, ER_CONN_SOCKET_ACCEPT_FAILED, msg_buff);
927 #else
928       LogErr(ERROR_LEVEL, ER_CONN_SOCKET_ACCEPT_FAILED, strerror(errno));
929 #endif
930     }
931     if (socket_errno == SOCKET_ENFILE || socket_errno == SOCKET_EMFILE)
932       sleep(1);  // Give other threads some time
933     return true;
934   }
935 
936   return false;
937 }
938 
939 #ifdef HAVE_LIBWRAP
940 /**
941   Ask TCP wrapper whether an accepted connection is allowed.
942 
943   @param connect_sock  Socket corresponding to accepted connection
944 
945   @return operation result
946     @retval true  connection is prohibited by TCP wrapper's policy
947     @retval false  connection is allowed by TCP wrapper's policy
948 */
check_connection_refused_by_tcp_wrapper(MYSQL_SOCKET connect_sock)949 bool check_connection_refused_by_tcp_wrapper(MYSQL_SOCKET connect_sock) {
950   struct request_info req;
951   signal(SIGCHLD, SIG_DFL);
952   request_init(&req, RQ_DAEMON, libwrap_name, RQ_FILE,
953                mysql_socket_getfd(connect_sock), NULL);
954   fromhost(&req);
955 
956   if (!hosts_access(&req)) {
957     /*
958       This may be stupid but refuse() includes an exit(0)
959       which we surely don't want...
960       clean_exit() - same stupid thing ...
961 
962       We're using syslog() here instead of my_syslog()
963       as this lets us pass in a facility that may differ
964       from that used by the error logging component.
965       This is unproblematic as TCP-wrapper is unix specific,
966       anyway.
967     */
968     syslog(LOG_AUTH | LOG_WARNING, "refused connect from %s",
969            eval_client(&req));
970 
971 #ifdef HAVE_LIBWRAP_PROTOTYPES
972     // Some distros have patched tcpd.h to have proper prototypes
973     if (req.sink) (req.sink)(req.fd);
974 #else
975     // Some distros have not patched tcpd.h
976     if (req.sink) ((void (*)(int))req.sink)(req.fd);
977 #endif
978     /*
979       The connection was refused by TCP wrappers.
980       There are no details (by client IP) available to update the host_cache.
981     */
982     mysql_socket_shutdown(connect_sock, SHUT_RDWR);
983     mysql_socket_close(connect_sock);
984 
985     connection_errors_tcpwrap++;
986     return true;
987   }
988 
989   return false;
990 }
991 #endif  // HAVE_LIBWRAP
992 
993 static my_thread_handle admin_socket_thread_id;
994 static my_thread_attr_t admin_socket_thread_attrib;
995 static bool admin_thread_started = false;
996 
997 static mysql_mutex_t LOCK_start_admin_thread;
998 static mysql_cond_t COND_start_admin_thread;
999 
1000 #ifdef HAVE_PSI_INTERFACE
1001 static PSI_mutex_key key_LOCK_start_admin_thread;
1002 static PSI_cond_key key_COND_start_admin_thread;
1003 
1004 static PSI_mutex_info admin_socket_thread_mutexes[] = {
1005     {&key_LOCK_start_admin_thread, "LOCK_start_admin_thread",
1006      PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}};
1007 
1008 static PSI_cond_info admin_socket_thread_conds[] = {
1009     {&key_COND_start_admin_thread, "COND_start_admin_thread",
1010      PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}};
1011 
init_psi_keys(void)1012 static inline void init_psi_keys(void) {
1013   const char *category = "sql";
1014   int count = array_elements(admin_socket_thread_mutexes);
1015   mysql_mutex_register(category, admin_socket_thread_mutexes, count);
1016 
1017   count = array_elements(admin_socket_thread_conds);
1018   mysql_cond_register(category, admin_socket_thread_conds, count);
1019 }
1020 #endif
1021 
1022 /**
1023   Signal to a spawning thread that spawned thread has been started.
1024 */
mark_admin_thread_started()1025 static inline void mark_admin_thread_started() {
1026   mysql_mutex_lock(&LOCK_start_admin_thread);
1027   admin_thread_started = true;
1028   mysql_cond_signal(&COND_start_admin_thread);
1029   mysql_mutex_unlock(&LOCK_start_admin_thread);
1030 }
1031 
1032 /**
1033   Wait until admin thread has started execution.
1034 */
wait_for_admin_thread_started()1035 static inline void wait_for_admin_thread_started() {
1036   mysql_mutex_lock(&LOCK_start_admin_thread);
1037   while (!admin_thread_started)
1038     mysql_cond_wait(&COND_start_admin_thread, &LOCK_start_admin_thread);
1039   mysql_mutex_unlock(&LOCK_start_admin_thread);
1040 }
1041 
1042 /**
1043   Listen to admin interface and accept incoming connection on it.
1044   This function is run in a separate thread.
1045 
1046   @return operation result. false on success, true on error
1047 */
handle_admin_socket(MYSQL_SOCKET admin_socket,const std::string & network_namespace_for_listening_socket)1048 static bool handle_admin_socket(
1049     /// pointer to a socket for listening to admin interface
1050     MYSQL_SOCKET admin_socket
1051 #ifdef HAVE_SETNS
1052     ,
1053     /// network namespace associated with the socket
1054     const std::string &network_namespace_for_listening_socket
1055 #endif
1056 ) {
1057   mark_admin_thread_started();
1058 
1059 #ifdef HAVE_POLL
1060   static const int NUMBER_OF_POLLED_FDS = 1;
1061   struct pollfd fds[NUMBER_OF_POLLED_FDS] = {
1062       {mysql_socket_getfd(admin_socket), POLLIN, 0}};
1063 #else
1064   fd_set client_fds;
1065   FD_ZERO(&client_fds);
1066   FD_SET(mysql_socket_getfd(admin_socket), &client_fds);
1067   int max_used_connection = mysql_socket_getfd(admin_socket);
1068 #endif
1069 
1070   while (!connection_events_loop_aborted()) {
1071 #ifdef HAVE_POLL
1072     int retval = poll(fds, NUMBER_OF_POLLED_FDS, -1);
1073 #else
1074     fd_set read_fds = client_fds;
1075     int retval = select(max_used_connection, &read_fds, 0, 0, 0);
1076 #endif
1077 
1078     if (retval < 0 && socket_errno != SOCKET_EINTR) {
1079       /*
1080         select(2)/poll(2) failed on the listening port.
1081         There is not much details to report about the client,
1082         increment the server global status variable.
1083       */
1084       ++connection_errors_select;
1085       if (!select_errors++ && !connection_events_loop_aborted())
1086         LogErr(ERROR_LEVEL, ER_CONN_SOCKET_SELECT_FAILED, socket_errno);
1087     }
1088 
1089     if (retval < 0) return true;
1090 
1091     if (connection_events_loop_aborted()) return false;
1092 
1093 #ifdef HAVE_SETNS
1094     /*
1095       If a network namespace is specified for a listening socket then set this
1096       network namespace as active before call to accept().
1097       It is not clear from manuals whether a socket returned by a call to
1098       accept() borrows a network namespace from a server socket used for
1099       accepting a new connection. For that reason, assign a network namespace
1100       explicitly before calling accept().
1101     */
1102     if (!network_namespace_for_listening_socket.empty() &&
1103         set_network_namespace(network_namespace_for_listening_socket))
1104       return true;
1105 #endif
1106 
1107     MYSQL_SOCKET connect_sock;
1108     bool accept_retval = accept_connection(admin_socket, &connect_sock);
1109 
1110 #ifdef HAVE_SETNS
1111     if (!network_namespace_for_listening_socket.empty() &&
1112         restore_original_network_namespace())
1113       return true;
1114 #endif
1115 
1116     if (accept_retval) continue;
1117 
1118 #ifdef HAVE_LIBWRAP
1119     if (check_connection_refused_by_tcp_wrapper(connect_sock)) return true;
1120 #endif  // HAVE_LIBWRAP
1121 
1122     Channel_info_tcpip_socket *channel_info =
1123         new (std::nothrow) Channel_info_tcpip_socket(connect_sock, true);
1124     if (channel_info == nullptr) {
1125       (void)mysql_socket_shutdown(connect_sock, SHUT_RDWR);
1126       (void)mysql_socket_close(connect_sock);
1127       connection_errors_internal++;
1128       return true;
1129     }
1130 
1131 #ifdef HAVE_SETNS
1132     if (!network_namespace_for_listening_socket.empty())
1133       channel_info->set_network_namespace(
1134           network_namespace_for_listening_socket);
1135 #endif
1136 
1137     Connection_handler_manager *mgr =
1138         Connection_handler_manager::get_instance();
1139 
1140     if (channel_info != nullptr) mgr->process_new_connection(channel_info);
1141   }
1142 
1143   return false;
1144 }
1145 
1146 using admin_thread_arg_t = std::pair<MYSQL_SOCKET, std::string>;
1147 
1148 /**
1149   Initialize thread's internal structures, run thread loop,
1150   deinitialize thread's internal structure on thread exit.
1151 
1152   @param arg  pointer to a socket for listening to admin interface
1153 */
admin_socket_thread(void * arg)1154 extern "C" void *admin_socket_thread(void *arg) {
1155   my_thread_init();
1156 
1157   std::unique_ptr<admin_thread_arg_t> arg_for_admin_socket_thread(
1158       (admin_thread_arg_t *)arg);
1159 
1160   (void)handle_admin_socket(arg_for_admin_socket_thread->first
1161 #ifdef HAVE_SETNS
1162                             ,
1163                             arg_for_admin_socket_thread->second
1164 #endif
1165   );
1166 
1167   my_thread_end();
1168   my_thread_exit(nullptr);
1169 
1170   return nullptr;
1171 }
1172 
1173 /**
1174   Initialize context required for running a thread handling connection requests
1175   on admin interface. Such context include mutex LOCK_start_admin_thread,
1176   condition variable COND_start_admin_thread and attributes used for thread
1177   spawning.
1178 */
initialize_thread_context()1179 static inline void initialize_thread_context() {
1180 #ifdef HAVE_PSI_INTERFACE
1181   init_psi_keys();
1182 #endif
1183 
1184   mysql_mutex_init(key_LOCK_start_admin_thread, &LOCK_start_admin_thread,
1185                    MY_MUTEX_INIT_FAST);
1186   mysql_cond_init(key_COND_start_admin_thread, &COND_start_admin_thread);
1187 
1188   (void)my_thread_attr_init(&admin_socket_thread_attrib);
1189   my_thread_attr_setdetachstate(&admin_socket_thread_attrib,
1190                                 MY_THREAD_CREATE_JOINABLE);
1191 #ifndef _WIN32
1192   pthread_attr_setscope(&admin_socket_thread_attrib, PTHREAD_SCOPE_SYSTEM);
1193 #endif
1194 }
1195 
1196 /**
1197   Spawn a thread for handling incoming connections request on admin interface.
1198 
1199   @param admin_socket  A socket to listen corresponding admin interface.
1200   @param network_namespace Network namespace to use for communicating
1201                            via admin socket
1202   @return Operation result. false on success, true on failure.
1203 */
spawn_admin_thread(MYSQL_SOCKET admin_socket,const std::string & network_namespace)1204 static inline bool spawn_admin_thread(MYSQL_SOCKET admin_socket,
1205                                       const std::string &network_namespace) {
1206   initialize_thread_context();
1207 
1208   admin_thread_arg_t *arg_for_admin_socket_thread =
1209       new (std::nothrow) admin_thread_arg_t(admin_socket, network_namespace);
1210 
1211   if (arg_for_admin_socket_thread == nullptr) return true;
1212 
1213   int ret = mysql_thread_create(
1214       key_thread_handle_con_admin_sockets, &admin_socket_thread_id,
1215       &admin_socket_thread_attrib, admin_socket_thread,
1216       (void *)arg_for_admin_socket_thread);
1217 
1218   (void)my_thread_attr_destroy(&admin_socket_thread_attrib);
1219 
1220   if (ret) {
1221     LogErr(ERROR_LEVEL, ER_CANT_CREATE_ADMIN_THREAD, errno);
1222     return true;
1223   }
1224 
1225   wait_for_admin_thread_started();
1226 
1227   return false;
1228 }
1229 
setup_listener()1230 bool Mysqld_socket_listener::setup_listener() {
1231   /*
1232     It's matter to add a socket for admin connection listener firstly,
1233     before listening sockets for other connection types be added.
1234     It is done in order to check availability of new incoming connection
1235     on admin interface with higher priority than on other interfaces..
1236   */
1237   if (!m_admin_bind_address.address.empty()) {
1238     TCP_socket tcp_socket(m_admin_bind_address.address,
1239                           m_admin_bind_address.network_namespace,
1240                           m_admin_tcp_port, m_backlog, m_port_timeout);
1241 
1242     MYSQL_SOCKET mysql_socket = tcp_socket.get_listener_socket();
1243     if (mysql_socket.fd == INVALID_SOCKET) return true;
1244 
1245     m_admin_interface_listen_socket = mysql_socket;
1246 
1247     if (m_use_separate_thread_for_admin) {
1248       if (spawn_admin_thread(m_admin_interface_listen_socket,
1249                              m_admin_bind_address.network_namespace))
1250         return true;
1251     } else {
1252       m_socket_vector.emplace_back(mysql_socket, Socket_type::TCP_SOCKET,
1253                                    &m_admin_bind_address.network_namespace,
1254                                    Socket_interface_type::ADMIN_INTERFACE);
1255     }
1256   }
1257 
1258   // Setup tcp socket listener
1259   if (m_tcp_port) {
1260     for (const auto &bind_address_info : m_bind_addresses) {
1261       TCP_socket tcp_socket(bind_address_info.address,
1262                             bind_address_info.network_namespace, m_tcp_port,
1263                             m_backlog, m_port_timeout);
1264 
1265       MYSQL_SOCKET mysql_socket = tcp_socket.get_listener_socket();
1266       if (mysql_socket.fd == INVALID_SOCKET) return true;
1267       m_socket_vector.emplace_back(mysql_socket, Socket_type::TCP_SOCKET,
1268                                    &bind_address_info.network_namespace,
1269                                    Socket_interface_type::DEFAULT_INTERFACE);
1270     }
1271   }
1272 #if defined(HAVE_SYS_UN_H)
1273   // Setup unix socket listener
1274   if (m_unix_sockname != "") {
1275     Unix_socket unix_socket(&m_unix_sockname, m_backlog);
1276 
1277     MYSQL_SOCKET mysql_socket = unix_socket.get_listener_socket();
1278     if (mysql_socket.fd == INVALID_SOCKET) return true;
1279     Listen_socket s(mysql_socket, Socket_type::UNIX_SOCKET);
1280     m_socket_vector.push_back(s);
1281     m_unlink_sockname = true;
1282   }
1283 #endif /* HAVE_SYS_UN_H */
1284 
1285   setup_connection_events(m_socket_vector);
1286 
1287   return false;
1288 }
1289 
get_listen_socket() const1290 const Listen_socket *Mysqld_socket_listener::get_listen_socket() const {
1291 /*
1292   In case admin interface was set up, then first check whether an admin socket
1293   ready to accept a new connection. Doing this way provides higher priority
1294   to admin interface over other listeners.
1295 */
1296 #ifdef HAVE_POLL
1297   uint start_index = 0;
1298   if (!m_admin_bind_address.address.empty() &&
1299       !m_use_separate_thread_for_admin) {
1300     if (m_poll_info.m_fds[0].revents & POLLIN) {
1301       return &m_socket_vector[0];
1302     } else
1303       start_index = 1;
1304   }
1305 
1306   for (uint i = start_index; i < m_socket_vector.size(); ++i) {
1307     if (m_poll_info.m_fds[i].revents & POLLIN) {
1308       return &m_socket_vector[i];
1309     }
1310   }
1311 
1312 #else  // HAVE_POLL
1313   if (!m_admin_bind_address.address.empty() &&
1314       !m_use_separate_thread_for_admin &&
1315       FD_ISSET(mysql_socket_getfd(m_admin_interface_listen_socket),
1316                &m_select_info.m_read_fds)) {
1317     return &m_socket_vector[0];
1318   }
1319 
1320   for (const auto &socket_element : m_socket_vector) {
1321     if (FD_ISSET(mysql_socket_getfd(socket_element.m_socket),
1322                  &m_select_info.m_read_fds)) {
1323       return &socket_element;
1324     }
1325   }
1326 
1327 #endif  // HAVE_POLL
1328   return nullptr;
1329   ;
1330 }
1331 
listen_for_connection_event()1332 Channel_info *Mysqld_socket_listener::listen_for_connection_event() {
1333 #ifdef HAVE_POLL
1334   int retval = poll(&m_poll_info.m_fds[0], m_socket_vector.size(), -1);
1335 #else
1336   m_select_info.m_read_fds = m_select_info.m_client_fds;
1337   int retval = select((int)m_select_info.m_max_used_connection,
1338                       &m_select_info.m_read_fds, 0, 0, 0);
1339 #endif
1340 
1341   if (retval < 0 && socket_errno != SOCKET_EINTR) {
1342     /*
1343       select(2)/poll(2) failed on the listening port.
1344       There is not much details to report about the client,
1345       increment the server global status variable.
1346     */
1347     ++connection_errors_select;
1348     if (!select_errors++ && !connection_events_loop_aborted())
1349       LogErr(ERROR_LEVEL, ER_CONN_SOCKET_SELECT_FAILED, socket_errno);
1350   }
1351 
1352   if (retval < 0 || connection_events_loop_aborted()) return nullptr;
1353 
1354   /* Is this a new connection request ? */
1355   const Listen_socket *listen_socket = get_listen_socket();
1356   /*
1357     When poll/select returns control flow then at least one ready server socket
1358     must exist. Check that get_ready_socket() returns a valid socket.
1359   */
1360   DBUG_ASSERT(listen_socket != nullptr);
1361   MYSQL_SOCKET connect_sock;
1362 #ifdef HAVE_SETNS
1363   /*
1364     If a network namespace is specified for a listening socket then set this
1365     network namespace as active before call to accept().
1366     It is not clear from manuals whether a socket returned by a call to
1367     accept() borrows a network namespace from a server socket used for
1368     accepting a new connection. For that reason, assign a network namespace
1369     explicitly before calling accept().
1370   */
1371   std::string network_namespace_for_listening_socket;
1372   if (listen_socket->m_socket_type == Socket_type::TCP_SOCKET) {
1373     network_namespace_for_listening_socket =
1374         (listen_socket->m_network_namespace != nullptr
1375              ? *listen_socket->m_network_namespace
1376              : std::string(""));
1377     if (!network_namespace_for_listening_socket.empty() &&
1378         set_network_namespace(network_namespace_for_listening_socket))
1379       return nullptr;
1380   }
1381 #endif
1382   if (accept_connection(listen_socket->m_socket, &connect_sock)) {
1383 #ifdef HAVE_SETNS
1384     if (!network_namespace_for_listening_socket.empty())
1385       (void)restore_original_network_namespace();
1386 #endif
1387     return nullptr;
1388   }
1389 
1390 #ifdef HAVE_SETNS
1391   if (!network_namespace_for_listening_socket.empty() &&
1392       restore_original_network_namespace())
1393     return nullptr;
1394 #endif
1395 
1396 #ifdef HAVE_LIBWRAP
1397   if ((listen_socket->m_socket_type == Socket_type::TCP_SOCKET) &&
1398       check_connection_refused_by_tcp_wrapper(connect_sock)) {
1399     return nullptr;
1400   }
1401 #endif  // HAVE_LIBWRAP
1402 
1403   Channel_info *channel_info = nullptr;
1404   if (listen_socket->m_socket_type == Socket_type::UNIX_SOCKET)
1405     channel_info = new (std::nothrow) Channel_info_local_socket(connect_sock);
1406   else
1407     channel_info = new (std::nothrow) Channel_info_tcpip_socket(
1408         connect_sock, (listen_socket->m_socket_interface ==
1409                        Socket_interface_type::ADMIN_INTERFACE));
1410   if (channel_info == nullptr) {
1411     (void)mysql_socket_shutdown(connect_sock, SHUT_RDWR);
1412     (void)mysql_socket_close(connect_sock);
1413     connection_errors_internal++;
1414     return nullptr;
1415   }
1416 
1417 #ifdef HAVE_SETNS
1418   if (listen_socket->m_socket_type == Socket_type::TCP_SOCKET &&
1419       !network_namespace_for_listening_socket.empty())
1420     static_cast<Channel_info_tcpip_socket *>(channel_info)
1421         ->set_network_namespace(network_namespace_for_listening_socket);
1422 #endif
1423   return channel_info;
1424 }
1425 
close_listener()1426 void Mysqld_socket_listener::close_listener() {
1427   for (const auto &socket_element : m_socket_vector) {
1428     (void)mysql_socket_shutdown(socket_element.m_socket, SHUT_RDWR);
1429     (void)mysql_socket_close(socket_element.m_socket);
1430   }
1431 
1432   /*
1433     In case a separate thread was spawned to handle incoming connection
1434     requests on admin interface, a socket corresponding to an admin interface
1435     being listened is not included in the m_socket_map. Instead, this socket
1436     referenced by the data member m_admin_interface_listen_socket.
1437   */
1438   if (m_use_separate_thread_for_admin) {
1439 #ifdef _WIN32
1440     /*
1441       For Windows, first close the socket referenced by the data member
1442       m_admin_interface_listen_socket. It results in return from select()
1443       API call running from a separate thread.
1444     */
1445     (void)mysql_socket_close(m_admin_interface_listen_socket);
1446     my_thread_join(&admin_socket_thread_id, nullptr);
1447 #else
1448     // First, finish listening thread.
1449     pthread_kill(admin_socket_thread_id.thread, SIGALRM);
1450     my_thread_join(&admin_socket_thread_id, nullptr);
1451     /*
1452       After a thread listening on admin interface finished, it is safe
1453       to close listening socket.
1454     */
1455     (void)mysql_socket_close(m_admin_interface_listen_socket);
1456 #endif
1457 
1458     mysql_mutex_destroy(&LOCK_start_admin_thread);
1459     mysql_cond_destroy(&COND_start_admin_thread);
1460   }
1461 
1462 #if defined(HAVE_SYS_UN_H)
1463   if (m_unix_sockname != "" && m_unlink_sockname) {
1464     std::string lock_filename = m_unix_sockname + ".lock";
1465     (void)unlink(lock_filename.c_str());
1466     (void)unlink(m_unix_sockname.c_str());
1467   }
1468 #endif
1469 
1470 #ifdef HAVE_SETNS
1471   release_network_namespace_resources();
1472 #endif
1473 
1474   m_socket_vector.clear();
1475   m_bind_addresses.clear();
1476 }
1477