-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathpacket.go
131 lines (112 loc) · 3.56 KB
/
packet.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package mysqlproto
import (
"fmt"
"strconv"
)
// https://dev.mysql.com/doc/internals/en/generic-response-packets.html
const (
OK_PACKET byte = 0x00
ERR_PACKET byte = 0xff
EOF_PACKET byte = 0xfe
)
type Packet struct {
SequenceID byte
Payload []byte
}
type ERRPacket struct {
Header byte // always 0xff
ErrorCode uint16
SQLStateMarker string
SQLState string
ErrorMessage string
}
type OKPacket struct {
Header byte // 0x00 or 0xfe
AffectedRows uint64
LastInsertID uint64
StatusFlags uint16
Warnings uint16
Info string
SessionStateChanges string
}
// https://dev.mysql.com/doc/internals/en/packet-OK_Packet.html
func ParseOKPacket(data []byte, capabilityFlags uint32) (OKPacket, error) {
if len(data) == 0 || (data[0] != OK_PACKET && data[0] != EOF_PACKET) {
return OKPacket{}, fmt.Errorf("mysqlproto: invalid OK_PACKET payload: %x", data)
}
offset := 0
header := data[offset]
offset += 1
affectedRows, offsetInt, _ := lenDecInt(data[1:])
offset += int(offsetInt)
lastInsertID, offsetInt, _ := lenDecInt(data[offset:])
offset += int(offsetInt)
var statusFlags, warnings uint16
if capabilityFlags&CLIENT_PROTOCOL_41 > 0 {
statusFlags = uint16(data[offset]) | uint16(data[offset+1])<<8
warnings = uint16(data[offset+2]) | uint16(data[offset+3])<<8
offset += 4
} else if capabilityFlags&CLIENT_TRANSACTIONS > 0 {
statusFlags = uint16(data[offset]) | uint16(data[offset+1])<<8
offset += 2
}
var info, sessionStateChanges string
if capabilityFlags&CLIENT_SESSION_TRACK > 0 {
size, intOffset, _ := lenDecInt(data[offset:])
info = string(data[offset+int(intOffset) : offset+int(intOffset)+int(size)])
offset += int(intOffset) + int(size)
if statusFlags&SERVER_SESSION_STATE_CHANGED > 0 {
size, intOffset, _ = lenDecInt(data[offset:])
sessionStateChanges = string(data[offset+int(intOffset) : offset+int(intOffset)+int(size)])
offset += int(intOffset) + int(size)
}
} else {
// Documentation says that in this case info is string<EOF> type
// but apparently it's string<lenenc>
// https://github.com/mysql/mysql-server/blob/5.6/sql/protocol.cc#L248
// https://github.com/mysql/mysql-server/blob/5.6/sql/protocol.cc#L585
_, infoOffset, _ := lenDecInt(data[offset:])
info = string(data[offset+int(infoOffset):])
}
pkt := OKPacket{
Header: header,
AffectedRows: affectedRows,
LastInsertID: lastInsertID,
StatusFlags: statusFlags,
Warnings: warnings,
Info: info,
SessionStateChanges: sessionStateChanges,
}
return pkt, nil
}
// https://dev.mysql.com/doc/internals/en/packet-ERR_Packet.html
func ParseERRPacket(data []byte, capabilityFlags uint32) (ERRPacket, error) {
if len(data) == 0 || data[0] != ERR_PACKET {
return ERRPacket{}, fmt.Errorf("mysqlproto: invalid ERR_PACKET payload: %x", data)
}
pkt := ERRPacket{
Header: data[0],
ErrorCode: uint16(data[1]) | uint16(data[2])<<8,
}
offset := 3
if capabilityFlags&CLIENT_PROTOCOL_41 > 0 {
pkt.SQLStateMarker = string(data[3])
pkt.SQLState = string(data[4:9])
offset = 9
}
pkt.ErrorMessage = string(data[offset:])
return pkt, nil
}
// https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html
func (p ERRPacket) Error() string {
return "mysqlproto: Error: " + strconv.Itoa(int(p.ErrorCode)) +
" SQLSTATE: " + p.SQLState +
" Message: " + p.ErrorMessage
}
func parseError(data []byte, capabilityFlags uint32) error {
pkt, err := ParseERRPacket(data, capabilityFlags)
if err != nil {
return err
}
return pkt
}