package mysqlserver import ( "encoding/binary" "fmt" "io" ) const ( ProtocolVersion = 10 ServerVersion = "8.0.0-mysql-tunnel" AuthPluginNative = "mysql_native_password" AuthPluginSHA2 = "caching_sha2_password" MaxPacketSize = 16 << 20 ) const ( ComQuit byte = 0x01 ComInitDB byte = 0x02 ComQuery byte = 0x03 ComFieldList byte = 0x04 ComPing byte = 0x0e ComStmtPrepare byte = 0x16 ComStmtExecute byte = 0x17 ComStmtClose byte = 0x19 ComResetConnection byte = 0x1f ) const ( clientLongPassword uint32 = 1 clientFoundRows uint32 = 1 << 1 clientLongFlag uint32 = 1 << 2 clientConnectWithDB uint32 = 1 << 3 clientProtocol41 uint32 = 1 << 9 clientSSL uint32 = 1 << 11 clientTransactions uint32 = 1 << 13 clientSecureConnection uint32 = 1 << 15 clientMultiStatements uint32 = 1 << 16 clientMultiResults uint32 = 1 << 17 clientPSMultiResults uint32 = 1 << 18 clientPluginAuth uint32 = 1 << 19 clientConnectAttrs uint32 = 1 << 20 clientPluginAuthLenencClientData uint32 = 1 << 21 clientSessionTrack uint32 = 1 << 23 clientDeprecateEOF uint32 = 1 << 24 ) const ( serverStatusAutocommit uint16 = 0x0002 ) const ( charsetUTF8MB4 uint8 = 45 charsetBinary uint8 = 63 ) const ( typeDecimal byte = 0 typeTiny byte = 1 typeShort byte = 2 typeLong byte = 3 typeFloat byte = 4 typeDouble byte = 5 typeNull byte = 6 typeTimestamp byte = 7 typeLongLong byte = 8 typeInt24 byte = 9 typeDate byte = 10 typeTime byte = 11 typeDatetime byte = 12 typeYear byte = 13 typeJSON byte = 245 typeNewDecimal byte = 246 typeTinyBlob byte = 249 typeMediumBlob byte = 250 typeLongBlob byte = 251 typeBlob byte = 252 typeVarString byte = 253 typeString byte = 254 ) const ( flagNotNull uint16 = 1 flagPriKey uint16 = 2 flagUniqueKey uint16 = 4 flagMultipleKey uint16 = 8 flagBlob uint16 = 16 flagUnsigned uint16 = 32 flagZerofill uint16 = 64 flagBinary uint16 = 128 flagEnum uint16 = 256 flagAutoIncrement uint16 = 512 flagTimestamp uint16 = 1024 flagSet uint16 = 2048 ) const ( errAccessDenied uint16 = 1045 errUnknownCommand uint16 = 1047 errNoDB uint16 = 1046 errParseError uint16 = 1064 errUnknown uint16 = 1105 errNotSupported uint16 = 1235 ) const ( markerOK byte = 0x00 markerEOF byte = 0xfe markerERR byte = 0xff markerNull byte = 0xfb ) var serverCapabilities = clientLongPassword | clientFoundRows | clientLongFlag | clientConnectWithDB | clientProtocol41 | clientTransactions | clientSecureConnection | clientMultiResults | clientPluginAuth | clientPluginAuthLenencClientData | clientConnectAttrs | clientDeprecateEOF | clientSessionTrack func appendU24(dst []byte, n uint32) []byte { return append(dst, byte(n), byte(n>>8), byte(n>>16)) } func appendU32(dst []byte, n uint32) []byte { b := make([]byte, 4) binary.LittleEndian.PutUint32(b, n) return append(dst, b...) } func appendU16(dst []byte, n uint16) []byte { return append(dst, byte(n), byte(n>>8)) } func appendNUL(dst []byte, s string) []byte { dst = append(dst, s...) return append(dst, 0) } func appendLenencInt(dst []byte, n uint64) []byte { switch { case n < 251: return append(dst, byte(n)) case n < 1<<16: return append(dst, 0xfc, byte(n), byte(n>>8)) case n < 1<<24: return append(dst, 0xfd, byte(n), byte(n>>8), byte(n>>16)) default: b := make([]byte, 8) binary.LittleEndian.PutUint64(b, n) return append(append(dst, 0xfe), b...) } } func appendLenencString(dst []byte, s []byte) []byte { dst = appendLenencInt(dst, uint64(len(s))) return append(dst, s...) } func readU24(b []byte) uint32 { return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 } func readLenencInt(b []byte, off int) (uint64, int, error) { if off >= len(b) { return 0, off, io.ErrUnexpectedEOF } switch b[off] { case 0xfb: return 0, off + 1, fmt.Errorf("NULL lenenc") case 0xfc: if off+3 > len(b) { return 0, off, io.ErrUnexpectedEOF } return uint64(b[off+1]) | uint64(b[off+2])<<8, off + 3, nil case 0xfd: if off+4 > len(b) { return 0, off, io.ErrUnexpectedEOF } return uint64(b[off+1]) | uint64(b[off+2])<<8 | uint64(b[off+3])<<16, off + 4, nil case 0xfe: if off+9 > len(b) { return 0, off, io.ErrUnexpectedEOF } return binary.LittleEndian.Uint64(b[off+1 : off+9]), off + 9, nil default: return uint64(b[off]), off + 1, nil } } func readLenencString(b []byte, off int) ([]byte, int, error) { n, off, err := readLenencInt(b, off) if err != nil { return nil, off, err } end := off + int(n) if end > len(b) { return nil, off, io.ErrUnexpectedEOF } return b[off:end], end, nil } func readNUL(b []byte, off int) (string, int, error) { for i := off; i < len(b); i++ { if b[i] == 0 { return string(b[off:i]), i + 1, nil } } return string(b[off:]), len(b), nil } func sqlState(errno uint16) string { switch errno { case errAccessDenied: return "28000" case errParseError: return "42000" case errNoDB: return "3D000" case errUnknownCommand, errNotSupported: return "HY000" default: return "HY000" } } func encodeOK(affected, lastInsertID uint64, status, warnings uint16) []byte { b := []byte{markerOK} b = appendLenencInt(b, affected) b = appendLenencInt(b, lastInsertID) b = appendU16(b, status) b = appendU16(b, warnings) return b } func encodeERR(errno uint16, msg string) []byte { b := []byte{markerERR} b = appendU16(b, errno) b = append(b, '#') state := sqlState(errno) if len(state) != 5 { state = "HY000" } b = append(b, state...) b = append(b, msg...) return b } func encodeEOF(warnings, status uint16) []byte { b := []byte{markerEOF} b = appendU16(b, warnings) b = appendU16(b, status) return b } func isNumericType(t byte) bool { switch t { case typeDecimal, typeTiny, typeShort, typeLong, typeFloat, typeDouble, typeNull, typeLongLong, typeInt24, typeYear, typeNewDecimal, typeBit: return true } return false } const typeBit byte = 16