Skip to content

Commit f77f6ff

Browse files
committed
Refactor packet parsing and authentication
1 parent 9ed0331 commit f77f6ff

File tree

2 files changed

+347
-244
lines changed

2 files changed

+347
-244
lines changed

packages/wp-mysql-proxy/src/class-mysql-protocol.php

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -229,18 +229,20 @@ class MySQL_Protocol {
229229
* @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html
230230
*
231231
* @param int $sequence_id The sequence ID of the packet.
232+
* @param int $server_status The status flags representing the server state.
232233
* @param int $affected_rows Number of rows affected by the query.
233234
* @param int $last_insert_id The last insert ID.
234-
* @param int $server_status The status flags representing the server state.
235235
* @param int $warning_count The warning count.
236+
* @param int $packet_header The packet header, indicating an OK or EOF semantic.
236237
* @return string The OK packet.
237238
*/
238239
public static function build_ok_packet(
239240
int $sequence_id,
240-
int $affected_rows,
241-
int $last_insert_id,
242241
int $server_status,
243-
int $warning_count
242+
int $affected_rows = 0,
243+
int $last_insert_id = 0,
244+
int $warning_count = 0,
245+
int $packet_header = self::OK_PACKET_HEADER
244246
): string {
245247
/**
246248
* Assemble the OK packet payload.
@@ -255,16 +257,47 @@ public static function build_ok_packet(
255257
*/
256258
$payload = pack(
257259
'Ca*a*vv',
258-
self::OK_PACKET_HEADER, // (C) OK packet header.
260+
$packet_header, // (C) OK packet header.
259261
self::encode_length_encoded_int( $affected_rows ), // (a*) Affected rows.
260262
self::encode_length_encoded_int( $last_insert_id ), // (a*) Last insert ID.
261263
$server_status, // (v) Server status flags.
262264
$warning_count, // (v) Server status flags.
263-
// No human-readable message for simplicity
264265
);
265266
return self::build_packet( $sequence_id, $payload );
266267
}
267268

269+
/**
270+
* Build the OK packet with an EOF header.
271+
*
272+
* When the CLIENT_DEPRECATE_EOF capability is supported, an OK packet with
273+
* an EOF header is used to mark EOF, instead of the deprecated EOF packet.
274+
*
275+
* @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html
276+
*
277+
* @param int $sequence_id The sequence ID of the packet.
278+
* @param int $server_status The status flags representing the server state.
279+
* @param int $affected_rows Number of rows affected by the query.
280+
* @param int $last_insert_id The last insert ID.
281+
* @param int $warning_count The warning count.
282+
* @return string The OK packet.
283+
*/
284+
public static function build_ok_packet_as_eof(
285+
int $sequence_id,
286+
int $server_status,
287+
int $affected_rows = 0,
288+
int $last_insert_id = 0,
289+
int $warning_count = 0
290+
): string {
291+
return self::build_ok_packet(
292+
$sequence_id,
293+
$server_status,
294+
$affected_rows,
295+
$last_insert_id,
296+
$warning_count,
297+
self::EOF_PACKET_HEADER
298+
);
299+
}
300+
268301
/**
269302
* Build the ERR packet.
270303
*
@@ -316,7 +349,7 @@ public static function build_err_packet(
316349
public static function build_eof_packet(
317350
int $sequence_id,
318351
int $server_status,
319-
int $warning_count
352+
int $warning_count = 0
320353
): string {
321354
/**
322355
* Assemble the EOF packet payload.
@@ -534,4 +567,68 @@ public static function encode_length_encoded_int( int $value ): string {
534567
public static function encode_length_encoded_string( string $value ): string {
535568
return self::encode_length_encoded_int( strlen( $value ) ) . $value;
536569
}
570+
571+
/**
572+
* Read MySQL length-encoded integer from a payload and advance the offset.
573+
*
574+
* @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html
575+
*
576+
* @param string $payload A payload of bytes to read from.
577+
* @param int $offset And offset to start reading from within the payload.
578+
* The value will be advanced by the number of bytes read.
579+
* @return int The decoded integer value.
580+
*/
581+
public static function read_length_encoded_int( string $payload, int &$offset ): int {
582+
$first_byte = ord( $payload[ $offset ] ?? "\0" );
583+
$offset += 1;
584+
585+
if ( $first_byte < 0xfb ) {
586+
$value = $first_byte;
587+
} elseif ( 0xfb === $first_byte ) {
588+
$value = 0;
589+
} elseif ( 0xfc === $first_byte ) {
590+
$value = unpack( 'v', $payload, $offset )[1];
591+
$offset += 2;
592+
} elseif ( 0xfd === $first_byte ) {
593+
$value = unpack( 'VX', $payload, $offset )[1];
594+
$offset += 3;
595+
} else {
596+
$value = unpack( 'P', $payload, $offset )[1];
597+
$offset += 8;
598+
}
599+
return $value;
600+
}
601+
602+
/**
603+
* Read MySQL length-encoded string from a payload and advance the offset.
604+
*
605+
* @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html
606+
*
607+
* @param string $payload A payload of bytes to read from.
608+
* @param int $offset And offset to start reading from within the payload.
609+
* The value will be advanced by the number of bytes read.
610+
* @return string The decoded string value.
611+
*/
612+
public static function read_length_encoded_string( string $payload, int &$offset ): string {
613+
$length = self::read_length_encoded_int( $payload, $offset );
614+
$value = substr( $payload, $offset, $length );
615+
$offset += $length;
616+
return $value;
617+
}
618+
619+
/**
620+
* Read MySQL null-terminated string from a payload and advance the offset.
621+
*
622+
* @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html
623+
*
624+
* @param string $payload A payload of bytes to read from.
625+
* @param int $offset And offset to start reading from within the payload.
626+
* The value will be advanced by the number of bytes read.
627+
* @return string The decoded string value.
628+
*/
629+
public static function read_null_terminated_string( string $payload, int &$offset ): string {
630+
$value = unpack( 'Z*', $payload, $offset )[1];
631+
$offset += strlen( $value ) + 1;
632+
return $value;
633+
}
537634
}

0 commit comments

Comments
 (0)