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