1 /* Copyright (c) 2011, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #include "binary_log_types.h"
24 
25 #include "statement_events.h"
26 
27 #include <algorithm>
28 #include <stdint.h>
29 
30 const unsigned char checksum_version_split[3]= {5, 6, 1};
31 const unsigned long checksum_version_product=
32   (checksum_version_split[0] * 256 + checksum_version_split[1]) * 256 +
33   checksum_version_split[2];
34 
35 
36 namespace binary_log_debug
37 {
38   bool debug_query_mts_corrupt_db_names= false;
39   bool debug_checksum_test= false;
40   bool debug_simulate_invalid_address= false;
41   bool debug_pretend_version_50034_in_binlog= false;
42 }
43 
44 namespace binary_log
45 {
46 /**
47    The method returns the checksum algorithm used to checksum the binary log.
48    For MySQL server versions < 5.6, the algorithm is undefined. For the higher
49    versions, the type is decoded from the FORMAT_DESCRIPTION_EVENT.
50 
51    @param buf buffer holding serialized FD event
52    @param len netto (possible checksum is stripped off) length of the event buf
53 
54    @return  the version-safe checksum alg descriptor where zero
55             designates no checksum, 255 - the orginator is
56             checksum-unaware (effectively no checksum) and the actuall
57             [1-254] range alg descriptor.
58 */
59 enum_binlog_checksum_alg
get_checksum_alg(const char * buf,unsigned long len)60 Log_event_footer::get_checksum_alg(const char* buf, unsigned long len)
61 {
62   enum_binlog_checksum_alg ret;
63   char version[ST_SERVER_VER_LEN];
64   unsigned char version_split[3];
65   BAPI_ASSERT(buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT);
66   memcpy(version, buf +
67          buf[LOG_EVENT_MINIMAL_HEADER_LEN + ST_COMMON_HEADER_LEN_OFFSET]
68          + ST_SERVER_VER_OFFSET, ST_SERVER_VER_LEN);
69   version[ST_SERVER_VER_LEN - 1]= 0;
70 
71   do_server_version_split(version, version_split);
72   if (version_product(version_split) < checksum_version_product)
73     ret=  BINLOG_CHECKSUM_ALG_UNDEF;
74   else
75     ret= static_cast<enum_binlog_checksum_alg>(*(buf + len -
76                                                  BINLOG_CHECKSUM_LEN -
77                                                  BINLOG_CHECKSUM_ALG_DESC_LEN));
78   BAPI_ASSERT(ret == BINLOG_CHECKSUM_ALG_OFF ||
79               ret == BINLOG_CHECKSUM_ALG_UNDEF ||
80               ret == BINLOG_CHECKSUM_ALG_CRC32);
81   return ret;
82 }
83 
84 /**
85   Log_event_header constructor
86 
87   @param buf                  the buffer containing the complete information
88                               including the event and the header data
89 
90   @param description_event    first constructor of Format_description_event,
91                               used to extract the binlog_version
92 */
93 Log_event_header::
Log_event_header(const char * buf,uint16_t binlog_version)94 Log_event_header(const char* buf, uint16_t binlog_version)
95 : data_written(0), log_pos(0)
96 {
97   uint32_t tmp_sec;
98   memcpy(&tmp_sec, buf, sizeof(tmp_sec));
99   when.tv_sec= le32toh(tmp_sec);
100   when.tv_usec= 0;
101   type_code= static_cast<Log_event_type>(buf[EVENT_TYPE_OFFSET]);
102   memcpy(&unmasked_server_id,
103          buf + SERVER_ID_OFFSET, sizeof(unmasked_server_id));
104 
105   unmasked_server_id= le32toh(unmasked_server_id);
106 
107   /**
108     @verbatim
109     The first 13 bytes in the header is as follows:
110       +============================================+
111       | member_variable               offset : len |
112       +============================================+
113       | when.tv_sec                        0 : 4   |
114       +--------------------------------------------+
115       | type_code       EVENT_TYPE_OFFSET(4) : 1   |
116       +--------------------------------------------+
117       | server_id       SERVER_ID_OFFSET(5)  : 4   |
118       +--------------------------------------------+
119       | data_written    EVENT_LEN_OFFSET(9)  : 4   |
120       +============================================+
121     @endverbatim
122    */
123   memcpy(&data_written, buf + EVENT_LEN_OFFSET, 4);
124   data_written= le64toh(data_written);
125 
126   memcpy(&log_pos, buf + LOG_POS_OFFSET, 4);
127   log_pos= le64toh(log_pos);
128 
129   switch (binlog_version)
130   {
131   case 1:
132     log_pos= 0;
133     flags= 0;
134     break;
135 
136   case 3:
137     /*
138       If the log is 4.0 (so here it can only be a 4.0 relay log read by
139       the SQL thread or a 4.0 master binlog read by the I/O thread),
140       log_pos is the beginning of the event: we transform it into the end
141       of the event, which is more useful.
142       But how do you know that the log is 4.0: you know it if
143       description_event is version 3 *and* you are not reading a
144       Format_desc (remember that mysqlbinlog starts by assuming that 5.0
145       logs are in 4.0 format, until it finds a Format_desc).
146     */
147     if (buf[EVENT_TYPE_OFFSET] < FORMAT_DESCRIPTION_EVENT && log_pos)
148     {
149       /*
150         If log_pos=0, don't change it. log_pos==0 is a marker to mean
151         "don't change rli->group_master_log_pos" (see
152         inc_group_relay_log_pos()). As it is unreal log_pos, adding the
153         event len's is not correct. For example, a fake Rotate event should
154         not have its log_pos (which is 0) changed or it will modify
155         Exec_master_log_pos in SHOW SLAVE STATUS, displaying a wrong
156         value of (a non-zero offset which does not exist in the master's
157         binlog, so which will cause problems if the user uses this value
158         in CHANGE MASTER).
159       */
160       log_pos+= data_written; /* purecov: inspected */
161     }
162 
163   /* 4.0 or newer */
164   /**
165     @verbatim
166     Additional header fields include:
167       +=============================================+
168       | member_variable               offset : len  |
169       +=============================================+
170       | log_pos           LOG_POS_OFFSET(13) : 4    |
171       +---------------------------------------------+
172       | flags               FLAGS_OFFSET(17) : 1    |
173       +---------------------------------------------+
174       | extra_headers                     19 : x-19 |
175       +=============================================+
176      extra_headers are not used in the current version.
177     @endverbatim
178    */
179     // Fall through.
180   default:
181     memcpy(&flags, buf + FLAGS_OFFSET, sizeof(flags));
182     flags= le16toh(flags);
183 
184      if ((buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) ||
185          (buf[EVENT_TYPE_OFFSET] == ROTATE_EVENT))
186      {
187        /*
188          These events always have a header which stops here (i.e. their
189          header is FROZEN).
190        */
191        /*
192          Initialization to zero of all other Log_event members as they're
193          not specified. Currently there are no such members; in the future
194          there will be an event UID (but Format_description and Rotate
195          don't need this UID, as they are not propagated through
196          --log-slave-updates (remember the UID is used to not play a query
197          twice when you have two masters which are slaves of a 3rd master).
198          Then we are done with decoding the header.
199       */
200       break;
201     }
202   /* otherwise, go on with reading the header from buf (nothing now) */
203   } //end switch (binlog_version)
204   BAPI_ASSERT(type_code < ENUM_END_EVENT || flags & LOG_EVENT_IGNORABLE_F);
205 }
206 
207 
208 /**
209   Tests the checksum algorithm used for the binary log, and asserts in case
210   if the checksum algorithm is invalid.
211 
212   @param   event_buf       point to the buffer containing serialized event
213   @param   event_len       length of the event accounting possible
214                            checksum alg
215   @param   alg             checksum algorithm used for the binary log
216 
217   @retval  true            if test fails
218   @retval  false           as success
219 */
event_checksum_test(unsigned char * event_buf,unsigned long event_len,enum_binlog_checksum_alg alg)220 bool Log_event_footer::event_checksum_test(unsigned char *event_buf,
221                                            unsigned long event_len,
222                                            enum_binlog_checksum_alg alg)
223 {
224   bool res= false;
225   unsigned short flags= 0; // to store in FD's buffer flags orig value
226 
227   if (alg != BINLOG_CHECKSUM_ALG_OFF && alg != BINLOG_CHECKSUM_ALG_UNDEF)
228   {
229     uint32_t incoming;
230     uint32_t computed;
231 
232     if (event_buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT)
233     {
234     #ifndef NDEBUG
235       unsigned char fd_alg= event_buf[event_len - BINLOG_CHECKSUM_LEN -
236                                       BINLOG_CHECKSUM_ALG_DESC_LEN];
237     #endif
238       /*
239         FD event is checksummed and therefore verified w/o
240         the binlog-in-use flag.
241       */
242       memcpy(&flags, event_buf + FLAGS_OFFSET, sizeof(flags));
243       flags= le16toh(flags);
244       if (flags & LOG_EVENT_BINLOG_IN_USE_F)
245         event_buf[FLAGS_OFFSET] &= ~LOG_EVENT_BINLOG_IN_USE_F;
246       /*
247          The only algorithm currently is CRC32. Zero indicates
248          the binlog file is checksum-free *except* the FD-event.
249       */
250     #ifndef NDEBUG
251       BAPI_ASSERT(fd_alg == BINLOG_CHECKSUM_ALG_CRC32 || fd_alg == 0);
252     #endif
253       BAPI_ASSERT(alg == BINLOG_CHECKSUM_ALG_CRC32);
254       /*
255         Complile time guard to watch over  the max number of alg
256       */
257       do_compile_time_assert(BINLOG_CHECKSUM_ALG_ENUM_END <= 0x80);
258     }
259     memcpy(&incoming,
260            event_buf + event_len - BINLOG_CHECKSUM_LEN, sizeof(incoming));
261     incoming= le32toh(incoming);
262 
263     computed= checksum_crc32(0L, NULL, 0);
264     /* checksum the event content but not the checksum part itself */
265     computed= binary_log::checksum_crc32(computed,
266                                          (const unsigned char*) event_buf,
267                                          event_len - BINLOG_CHECKSUM_LEN);
268 
269     if (flags != 0)
270     {
271       /* restoring the orig value of flags of FD */
272       BAPI_ASSERT(event_buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT);
273       event_buf[FLAGS_OFFSET]= static_cast<unsigned char>(flags);
274     }
275 
276     res= !(computed == incoming);
277   }
278 #ifndef NDEBUG
279   if (binary_log_debug::debug_checksum_test)
280     return true;
281 #endif
282   return res;
283 }
284 
285 
286 /**
287   This ctor will create a new object of Log_event_header, and initialize
288   the variable m_header, which in turn will be used to initialize Log_event's
289   member common_header.
290   It will also advance the buffer after decoding the header(it is done through
291   the constructor of Log_event_header) and
292   will be pointing to the start of event data
293 */
Binary_log_event(const char ** buf,uint16_t binlog_version,const char * server_version)294 Binary_log_event::Binary_log_event(const char **buf, uint16_t binlog_version,
295                                    const char *server_version)
296 : m_header(*buf, binlog_version)
297 {
298   m_footer= Log_event_footer();
299   //buf is advanced in Binary_log_event constructor to point to beginning of
300   //post-header
301   (*buf)+= LOG_EVENT_HEADER_LEN;
302 }
303 
304 /*
305   The destructor is pure virtual to prevent instantiation of the class.
306 */
~Binary_log_event()307 Binary_log_event::~Binary_log_event()
308 {
309 }
310 
311   /**
312     This event type should never occur. It is never written to a binary log.
313     If an event is read from a binary log that cannot be recognized as something
314     else, it is treated as Unknown_event.
315 
316     @param buf                Contains the serialized event.
317     @param description_event  An FDE event, used to get the following information
318                               -binlog_version
319                               -server_version
320                               -post_header_len
321                               -common_header_len
322                               The content of this object
323                               depends on the binlog-version currently in use.
324   */
Unknown_event(const char * buf,const Format_description_event * description_event)325 Unknown_event::Unknown_event(const char* buf,
326                 const Format_description_event *description_event)
327   : Binary_log_event(&buf,
328                      description_event->binlog_version,
329                      description_event->server_version)
330   {
331   }
332 #ifndef HAVE_MYSYS
print_event_info(std::ostream & info)333 void Binary_log_event::print_event_info(std::ostream& info) {}
print_long_info(std::ostream & info)334 void Binary_log_event::print_long_info(std::ostream& info) {}
335 /**
336   This method is used by the binlog_browser to print short and long
337   information about the event. Since the body of Stop_event is empty
338   the relevant information contains only the timestamp.
339   Please note this is different from the print_event_info methods
340   used by mysqlbinlog.cc.
341 
342   @param std output stream to which the event data is appended.
343 */
print_long_info(std::ostream & info)344 void Stop_event::print_long_info(std::ostream& info)
345 {
346   info << "Timestamp: " << header()->when.tv_sec;
347   this->print_event_info(info);
348 }
349 
print_event_info(std::ostream & info)350 void Unknown_event::print_event_info(std::ostream& info)
351 {
352   info << "Unhandled event";
353 }
354 
print_long_info(std::ostream & info)355 void Unknown_event::print_long_info(std::ostream& info)
356 {
357   info << "Timestamp: " << header()->when.tv_sec;
358   this->print_event_info(info);
359 }
360 
361 #endif
362 } // end namespace binary_log
363