mirror of
https://github.com/40t/go-sniffer.git
synced 2025-10-19 06:09:18 +08:00
implementl get user and database
Signed-off-by: zhuhuijun <zhuhuijunzhj@gmail.com>
This commit is contained in:
34
pkg/model/result.go
Normal file
34
pkg/model/result.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package model
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// MysqlQueryPiece 查询信息
|
||||
type MysqlQueryPiece struct {
|
||||
BaseQueryPiece
|
||||
|
||||
ClientHost string `json:"cip"`
|
||||
ClientPort string `json:"cport"`
|
||||
|
||||
VisitUser string `json:"user"`
|
||||
VisitDB string `json:"db"`
|
||||
QuerySQL string `json:"sql"`
|
||||
CostTimeInMS int64 `json:"cms"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// BaseQueryPiece 查询信息
|
||||
type BaseQueryPiece struct {
|
||||
ServerIP string `json:"sip"`
|
||||
ServerPort string `json:"sport"`
|
||||
CapturePacketRate float64 `json:"cpr"`
|
||||
EventTime int64 `json:"bt"`
|
||||
}
|
||||
|
||||
func (p MysqlQueryPiece) ToString() string {
|
||||
bytes, err := json.Marshal(&p)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(bytes)
|
||||
}
|
10
pkg/model/session.go
Normal file
10
pkg/model/session.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package model
|
||||
|
||||
type MysqlSession struct {
|
||||
UserName string
|
||||
DBName string
|
||||
ClientIP string
|
||||
ClientPort string
|
||||
ServerIP string
|
||||
ServerPort string
|
||||
}
|
161
pkg/parse/auth.go
Normal file
161
pkg/parse/auth.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package parse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMalformPacket = errors.New("malform packet error")
|
||||
)
|
||||
|
||||
type handshakeResponse41 struct {
|
||||
Capability uint32
|
||||
Collation uint8
|
||||
User string
|
||||
DBName string
|
||||
Auth []byte
|
||||
}
|
||||
|
||||
// AuthInfoParse parse username, dbname from mysql client auth info
|
||||
func AuthInfoParse(data []byte) (userName, dbName string, err error) {
|
||||
var resp handshakeResponse41
|
||||
pos, err := parseHandshakeResponseHeader(&resp, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Read the remaining part of the packet.
|
||||
if err = parseHandshakeResponseBody(&resp, data, pos); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
userName = resp.User
|
||||
dbName = resp.DBName
|
||||
return
|
||||
}
|
||||
|
||||
// parseHandshakeResponseHeader parses the common header of SSLRequest and HandshakeResponse41.
|
||||
func parseHandshakeResponseHeader(packet *handshakeResponse41, data []byte) (parsedBytes int, err error) {
|
||||
// Ensure there are enough data to read:
|
||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
|
||||
if len(data) < 4+4+1+23 {
|
||||
return 0, ErrMalformPacket
|
||||
}
|
||||
|
||||
offset := 0
|
||||
// capability
|
||||
capability := binary.LittleEndian.Uint32(data[:4])
|
||||
packet.Capability = capability
|
||||
offset += 4
|
||||
// skip max packet size
|
||||
offset += 4
|
||||
// charset, skip, if you want to use another charset, use set names
|
||||
packet.Collation = data[offset]
|
||||
offset++
|
||||
// skip reserved 23[00]
|
||||
offset += 23
|
||||
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// parseHandshakeResponseBody parse the HandshakeResponse (except the common header part).
|
||||
func parseHandshakeResponseBody(packet *handshakeResponse41, data []byte, offset int) (err error) {
|
||||
defer func() {
|
||||
// Check malformat packet cause out of range is disgusting, but don't panic!
|
||||
if r := recover(); r != nil {
|
||||
err = ErrMalformPacket
|
||||
}
|
||||
}()
|
||||
// user name
|
||||
packet.User = string(data[offset : offset+bytes.IndexByte(data[offset:], 0)])
|
||||
offset += len(packet.User) + 1
|
||||
|
||||
if packet.Capability&ClientPluginAuthLenencClientData > 0 {
|
||||
// MySQL client sets the wrong capability, it will set this bit even server doesn't
|
||||
// support ClientPluginAuthLenencClientData.
|
||||
// https://github.com/mysql/mysql-server/blob/5.7/sql-common/client.c#L3478
|
||||
num, null, off := parseLengthEncodedInt(data[offset:])
|
||||
offset += off
|
||||
if !null {
|
||||
packet.Auth = data[offset : offset+int(num)]
|
||||
offset += int(num)
|
||||
}
|
||||
} else if packet.Capability&ClientSecureConnection > 0 {
|
||||
// auth length and auth
|
||||
authLen := int(data[offset])
|
||||
offset++
|
||||
packet.Auth = data[offset : offset+authLen]
|
||||
offset += authLen
|
||||
} else {
|
||||
packet.Auth = data[offset : offset+bytes.IndexByte(data[offset:], 0)]
|
||||
offset += len(packet.Auth) + 1
|
||||
}
|
||||
|
||||
if packet.Capability&ClientConnectWithDB > 0 {
|
||||
if len(data[offset:]) > 0 {
|
||||
idx := bytes.IndexByte(data[offset:], 0)
|
||||
packet.DBName = string(data[offset : offset+idx])
|
||||
offset = offset + idx + 1
|
||||
}
|
||||
}
|
||||
|
||||
if packet.Capability&ClientPluginAuth > 0 {
|
||||
// TODO: Support mysql.ClientPluginAuth, skip it now
|
||||
idx := bytes.IndexByte(data[offset:], 0)
|
||||
offset = offset + idx + 1
|
||||
}
|
||||
|
||||
if packet.Capability&ClientConnectAtts > 0 {
|
||||
if len(data[offset:]) == 0 {
|
||||
// Defend some ill-formated packet, connection attribute is not important and can be ignored.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseLengthEncodedInt(b []byte) (num uint64, isNull bool, n int) {
|
||||
switch b[0] {
|
||||
// 251: NULL
|
||||
case 0xfb:
|
||||
n = 1
|
||||
isNull = true
|
||||
return
|
||||
|
||||
// 252: value of following 2
|
||||
case 0xfc:
|
||||
num = uint64(b[1]) | uint64(b[2])<<8
|
||||
n = 3
|
||||
return
|
||||
|
||||
// 253: value of following 3
|
||||
case 0xfd:
|
||||
num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16
|
||||
n = 4
|
||||
return
|
||||
|
||||
// 254: value of following 8
|
||||
case 0xfe:
|
||||
num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 |
|
||||
uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 |
|
||||
uint64(b[7])<<48 | uint64(b[8])<<56
|
||||
n = 9
|
||||
return
|
||||
}
|
||||
|
||||
// 0-250: value of first byte
|
||||
num = uint64(b[0])
|
||||
n = 1
|
||||
return
|
||||
}
|
||||
|
||||
func extractMysqlPayloadSize(header []byte) int {
|
||||
return int(uint32(header[0]) | uint32(header[1])<<8 | uint32(header[2])<<16)
|
||||
}
|
||||
|
||||
func bytesToInt(contents []byte) int {
|
||||
return int(uint32(contents[0]) | uint32(contents[1])<<8 | uint32(contents[2])<<16 | uint32(contents[3])<<24)
|
||||
}
|
56
pkg/parse/const.go
Normal file
56
pkg/parse/const.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package parse
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
maxSQLLen = 5 * 1024 * 1024
|
||||
)
|
||||
|
||||
// Client information.
|
||||
const (
|
||||
ClientLongPassword uint32 = 1 << iota
|
||||
ClientFoundRows
|
||||
ClientLongFlag
|
||||
ClientConnectWithDB
|
||||
ClientNoSchema
|
||||
ClientCompress
|
||||
ClientODBC
|
||||
ClientLocalFiles
|
||||
ClientIgnoreSpace
|
||||
ClientProtocol41
|
||||
ClientInteractive
|
||||
ClientSSL
|
||||
ClientIgnoreSigpipe
|
||||
ClientTransactions
|
||||
ClientReserved
|
||||
ClientSecureConnection
|
||||
ClientMultiStatements
|
||||
ClientMultiResults
|
||||
ClientPSMultiResults
|
||||
ClientPluginAuth
|
||||
ClientConnectAtts
|
||||
ClientPluginAuthLenencClientData
|
||||
)
|
||||
|
||||
// Auth name information.
|
||||
const (
|
||||
AuthName = "mysql_native_password"
|
||||
)
|
||||
|
||||
// MySQL database and tables.
|
||||
const (
|
||||
// SystemDB is the name of system database.
|
||||
SystemDB = "mysql"
|
||||
// UserTable is the table in system db contains user info.
|
||||
UserTable = "User"
|
||||
// DBTable is the table in system db contains db scope privilege info.
|
||||
DBTable = "DB"
|
||||
// GlobalVariablesTable is the table contains global system variables.
|
||||
GlobalVariablesTable = "GLOBAL_VARIABLES"
|
||||
// GlobalStatusTable is the table contains global status variables.
|
||||
GlobalStatusTable = "GLOBAL_STATUS"
|
||||
)
|
||||
|
||||
const (
|
||||
millSecondUnit = int64(time.Millisecond)
|
||||
)
|
5
pkg/parse/parse.go
Normal file
5
pkg/parse/parse.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package parse
|
||||
|
||||
func IsAuth(val byte) bool {
|
||||
return val == 133 || val == 15
|
||||
}
|
20
pkg/parse/session.go
Normal file
20
pkg/parse/session.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package parse
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
"go-sniffer/pkg/model"
|
||||
)
|
||||
|
||||
func GenSession(net, transport gopacket.Flow) *model.MysqlSession {
|
||||
clientIp := net.Src().String()
|
||||
sourceIp := net.Dst().String()
|
||||
clientPort := transport.Src().String()
|
||||
serverPort := transport.Dst().String()
|
||||
|
||||
return &model.MysqlSession{
|
||||
ClientIP: clientIp,
|
||||
ClientPort: clientPort,
|
||||
ServerIP: sourceIp,
|
||||
ServerPort: serverPort,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user