1 /*
2  * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
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
23  * 02110-1301  USA
24  */
25 
26 #include "xpl_server.h"
27 #include "xpl_session.h"
28 #include "mysql_show_variable_wrapper.h"
29 
30 #include "ngs_common/string_formatter.h"
31 #include "ngs/thread.h"
32 
33 #include "ngs/capabilities/configurator.h"
34 #include "ngs/capabilities/handler_readonly_value.h"
35 #include "cap_handles_expired_passwords.h"
36 
37 #include "mysqlx_version.h"
38 #include "mysql_variables.h"
39 #include "xpl_client.h"
40 
41 // needed for ip_to_hostname(), should probably be turned into a service
42 #include "hostname.h"
43 
44 
45 using namespace xpl;
46 
Client(ngs::Connection_ptr connection,ngs::Server_interface & server,Client_id client_id,Protocol_monitor * pmon)47 Client::Client(ngs::Connection_ptr connection, ngs::Server_interface &server, Client_id client_id,
48                Protocol_monitor *pmon)
49 : ngs::Client(connection, server, client_id, *pmon),
50   m_supports_expired_passwords(false),
51   m_protocol_monitor(pmon)
52 {
53   if (m_protocol_monitor)
54     m_protocol_monitor->init(this);
55 }
56 
57 
~Client()58 Client::~Client()
59 {
60   ngs::free_object(m_protocol_monitor);
61 }
62 
63 
on_session_close(ngs::Session_interface & s)64 void Client::on_session_close(ngs::Session_interface &s)
65 {
66   ngs::Client::on_session_close(s);
67   if (s.state_before_close() != ngs::Session_interface::Authenticating)
68   {
69     ++Global_status_variables::instance().m_closed_sessions_count;
70   }
71 }
72 
73 
on_session_reset(ngs::Session_interface & s)74 void Client::on_session_reset(ngs::Session_interface &s)
75 {
76   ngs::Client::on_session_reset(s);
77 }
78 
79 
set_supports_expired_passwords(bool flag)80 void Client::set_supports_expired_passwords(bool flag)
81 {
82   m_supports_expired_passwords = flag;
83 }
84 
supports_expired_passwords()85 bool Client::supports_expired_passwords()
86 {
87   return m_supports_expired_passwords;
88 }
89 
capabilities_configurator()90 ngs::Capabilities_configurator *Client::capabilities_configurator()
91 {
92   ngs::Capabilities_configurator *caps = ngs::Client::capabilities_configurator();
93 
94   // add our capabilities
95   caps->add_handler(ngs::allocate_shared<ngs::Capability_readonly_value>("node_type", "mysql"));
96   caps->add_handler(ngs::allocate_shared<Cap_handles_expired_passwords>(ngs::ref(*this)));
97 
98   return caps;
99 }
100 
get_session()101 ngs::shared_ptr<xpl::Session> Client::get_session()
102 {
103   return ngs::static_pointer_cast<xpl::Session>(session());
104 }
105 
106 
107 /** Close the client from another thread
108 
109 This can be called from any thread, so care must be taken to not call
110 anything that's not thread safe from here.
111  */
kill()112 void Client::kill()
113 {
114   if (m_state == Client_accepted)
115   {
116     disconnect_and_trigger_close();
117     return;
118   }
119 
120   m_session->on_kill();
121   ++Global_status_variables::instance().m_killed_sessions_count;
122 }
123 
124 
on_network_error(int error)125 void Client::on_network_error(int error)
126 {
127   ngs::Client::on_network_error(error);
128   if (error != 0)
129     ++Global_status_variables::instance().m_connection_errors_count;
130 }
131 
132 
on_server_shutdown()133 void Client::on_server_shutdown()
134 {
135   ngs::shared_ptr<ngs::Session_interface> local_copy = m_session;
136 
137   if (local_copy)
138     local_copy->on_kill();
139 
140   ngs::Client::on_server_shutdown();
141 }
142 
143 
on_auth_timeout()144 void Client::on_auth_timeout()
145 {
146   ngs::Client::on_auth_timeout();
147 
148   ++Global_status_variables::instance().m_connection_errors_count;
149 }
150 
151 
is_handler_thd(THD * thd)152 bool Client::is_handler_thd(THD *thd)
153 {
154   ngs::shared_ptr<ngs::Session_interface> session = this->session();
155 
156   return thd && session && (session->is_handled_by(thd));
157 }
158 
159 
get_status_ssl_cipher_list(st_mysql_show_var * var)160 void Client::get_status_ssl_cipher_list(st_mysql_show_var * var)
161 {
162   std::vector<std::string> ciphers = connection().options()->ssl_cipher_list();
163 
164   mysqld::xpl_show_var(var).assign(ngs::join(ciphers, ":"));
165 }
166 
167 
resolve_hostname()168 std::string Client::resolve_hostname()
169 {
170   std::string result;
171   std::string socket_ip_string;
172   uint16      socket_port;
173 
174   sockaddr_storage *addr = m_connection->peer_address(socket_ip_string, socket_port);
175 
176   if (NULL == addr)
177   {
178     log_error("%s: get peer address failed, can't resolve IP to hostname", m_id);
179     return "";
180   }
181 
182   char *hostname = NULL;
183   uint connect_errors = 0;
184   const int resolve_result = ip_to_hostname(addr, socket_ip_string.c_str(), &hostname, &connect_errors);
185 
186   if (RC_BLOCKED_HOST == resolve_result) {
187     throw std::runtime_error("Host is blocked");
188   }
189 
190   if (hostname) {
191     result = hostname;
192 
193     if (!is_localhost(hostname))
194       my_free(hostname);
195   }
196 
197   return result;
198 }
199 
200 
is_localhost(const char * hostname)201 bool Client::is_localhost(const char *hostname)
202 {
203   return hostname == mysqld::get_my_localhost();
204 }
205 
206 
init(Client * client)207 void Protocol_monitor::init(Client *client)
208 {
209   m_client = client;
210 }
211 
212 
213 namespace
214 {
215 template<xpl::Common_status_variables::Variable xpl::Common_status_variables::*variable>
update_status(ngs::shared_ptr<xpl::Session> session)216 inline void update_status(ngs::shared_ptr<xpl::Session> session)
217 {
218   if (session)
219     ++(session->get_status_variables().*variable);
220   ++(Global_status_variables::instance().*variable);
221 }
222 
223 
224 template<xpl::Common_status_variables::Variable xpl::Common_status_variables::*variable>
update_status(ngs::shared_ptr<xpl::Session> session,long param)225 inline void update_status(ngs::shared_ptr<xpl::Session> session, long param)
226 {
227   if (session)
228     (session->get_status_variables().*variable) += param;
229   (Global_status_variables::instance().*variable) += param;
230 }
231 } // namespace
232 
233 
on_notice_warning_send()234 void Protocol_monitor::on_notice_warning_send()
235 {
236   update_status<&Common_status_variables::m_notice_warning_sent>(m_client->get_session());
237 }
238 
239 
on_notice_other_send()240 void Protocol_monitor::on_notice_other_send()
241 {
242   update_status<&Common_status_variables::m_notice_other_sent>(m_client->get_session());
243 }
244 
245 
on_error_send()246 void Protocol_monitor::on_error_send()
247 {
248   update_status<&Common_status_variables::m_errors_sent>(m_client->get_session());
249 }
250 
251 
on_fatal_error_send()252 void Protocol_monitor::on_fatal_error_send()
253 {
254   ++Global_status_variables::instance().m_sessions_fatal_errors_count;
255 }
256 
257 
on_init_error_send()258 void Protocol_monitor::on_init_error_send()
259 {
260   ++Global_status_variables::instance().m_init_errors_count;
261 }
262 
263 
on_row_send()264 void Protocol_monitor::on_row_send()
265 {
266   update_status<&Common_status_variables::m_rows_sent>(m_client->get_session());
267 }
268 
269 
on_send(long bytes_transferred)270 void Protocol_monitor::on_send(long bytes_transferred)
271 {
272   update_status<&Common_status_variables::m_bytes_sent>(m_client->get_session(), bytes_transferred);
273 }
274 
275 
on_receive(long bytes_transferred)276 void Protocol_monitor::on_receive(long bytes_transferred)
277 {
278   update_status<&Common_status_variables::m_bytes_received>(m_client->get_session(), bytes_transferred);
279 }
280 
281 
on_error_unknown_msg_type()282 void Protocol_monitor::on_error_unknown_msg_type()
283 {
284   update_status<&Common_status_variables::m_errors_unknown_message_type>(m_client->get_session());
285 }
286