diff --git a/README.md b/README.md index 5c38e81..7955c19 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # go-sniffer -> Capture mysql,redis,http,mongodb etc protocol... +> Capture mysql,mssql,redis,http,mongodb etc protocol... > 抓包截取项目中的数据库请求并解析成相应的语句,如mysql协议会解析为sql语句,便于调试。 > 不要修改代码,直接嗅探项目中的数据请求。 @@ -14,6 +14,7 @@ - [Redis](#redis) - [Http](#http) - [Mongodb](#mongodb) +- [mssql](#mssql) - Kafka (developing) - ... @@ -104,6 +105,8 @@ $ go-sniffer lo0 mysql $ go-sniffer en0 redis $ go-sniffer eth0 http -p 8080 $ go-sniffer eth1 mongodb +$ go-sniffer eth0 mssql + ``` ## License: [MIT](http://opensource.org/licenses/MIT) diff --git a/core/cmd.go b/core/cmd.go index 9091cda..210765d 100644 --- a/core/cmd.go +++ b/core/cmd.go @@ -1,11 +1,11 @@ package core import ( - "os" - "strings" "fmt" "net" + "os" "strconv" + "strings" ) const InternalCmdPrefix = "--" @@ -18,14 +18,14 @@ const ( ) type Cmd struct { - Device string + Device string plugHandle *Plug } func NewCmd(p *Plug) *Cmd { return &Cmd{ - plugHandle:p, + plugHandle: p, } } @@ -55,27 +55,27 @@ func (cm *Cmd) parseInternalCmd() { cmd := strings.Trim(arg, InternalCmdPrefix) switch cmd { - case InternalCmdHelp: - cm.printHelpMessage() - break - case InternalCmdEnv: - fmt.Println("External plug-in path : "+cm.plugHandle.dir) - break - case InternalCmdList: - cm.plugHandle.PrintList() - break - case InternalCmdVer: - fmt.Println(cxt.Version) - break - case InternalDevice: - cm.printDevice() - break + case InternalCmdHelp: + cm.printHelpMessage() + break + case InternalCmdEnv: + fmt.Println("External plug-in path : " + cm.plugHandle.dir) + break + case InternalCmdList: + cm.plugHandle.PrintList() + break + case InternalCmdVer: + fmt.Println(cxt.Version) + break + case InternalDevice: + cm.printDevice() + break } os.Exit(1) } //usage -func (cm *Cmd) printHelpMessage() { +func (cm *Cmd) printHelpMessage() { fmt.Println("==================================================================================") fmt.Println("[Usage]") @@ -85,6 +85,7 @@ func (cm *Cmd) printHelpMessage() { fmt.Println(" [exp]") fmt.Println(" go-sniffer en0 redis Capture redis packet") fmt.Println(" go-sniffer en0 mysql -p 3306 Capture mysql packet") + fmt.Println(" go-sniffer en0 mssql -p 1433 Capture mssql packet") fmt.Println() fmt.Println(" go-sniffer --[commend]") fmt.Println(" --help \"this page\"") @@ -104,21 +105,21 @@ func (cm *Cmd) printHelpMessage() { func (cm *Cmd) printPlugList() { l := len(cm.plugHandle.InternalPlugList) l += len(cm.plugHandle.ExternalPlugList) - fmt.Println("# Number of plug-ins : "+strconv.Itoa(l)) + fmt.Println("# Number of plug-ins : " + strconv.Itoa(l)) } //print device func (cm *Cmd) printDevice() { - ifaces, err:= net.Interfaces() + ifaces, err := net.Interfaces() if err != nil { panic(err) } for _, iface := range ifaces { addrs, _ := iface.Addrs() - for _,a:=range addrs { + for _, a := range addrs { if ipnet, ok := a.(*net.IPNet); ok { if ip4 := ipnet.IP.To4(); ip4 != nil { - fmt.Println("[device] : "+iface.Name+" : "+iface.HardwareAddr.String()+" "+ip4.String()) + fmt.Println("[device] : " + iface.Name + " : " + iface.HardwareAddr.String() + " " + ip4.String()) } } } @@ -126,7 +127,7 @@ func (cm *Cmd) printDevice() { } //Parameters needed for plug-ins -func (cm *Cmd) parsePlugCmd() { +func (cm *Cmd) parsePlugCmd() { if len(os.Args) < 3 { fmt.Println("not found [Plug-in name]") @@ -134,12 +135,8 @@ func (cm *Cmd) parsePlugCmd() { os.Exit(1) } - cm.Device = os.Args[1] - plugName := os.Args[2] - plugParams:= os.Args[3:] + cm.Device = os.Args[1] + plugName := os.Args[2] + plugParams := os.Args[3:] cm.plugHandle.SetOption(plugName, plugParams) } - - - - diff --git a/core/plug.go b/core/plug.go index fe85a80..f5e531d 100644 --- a/core/plug.go +++ b/core/plug.go @@ -1,24 +1,25 @@ package core import ( + "fmt" + "io" "io/ioutil" + "path" + "path/filepath" "plugin" - "github.com/google/gopacket" - "io" - mysql "github.com/40t/go-sniffer/plugSrc/mysql/build" - redis "github.com/40t/go-sniffer/plugSrc/redis/build" + hp "github.com/40t/go-sniffer/plugSrc/http/build" mongodb "github.com/40t/go-sniffer/plugSrc/mongodb/build" - "path/filepath" - "fmt" - "path" + mssql "github.com/40t/go-sniffer/plugSrc/mssql/build" + mysql "github.com/40t/go-sniffer/plugSrc/mysql/build" + redis "github.com/40t/go-sniffer/plugSrc/redis/build" + "github.com/google/gopacket" ) type Plug struct { - - dir string + dir string ResolveStream func(net gopacket.Flow, transport gopacket.Flow, r io.Reader) - BPF string + BPF string InternalPlugList map[string]PlugInterface ExternalPlugList map[string]ExternalPlug @@ -48,7 +49,7 @@ func NewPlug() *Plug { var p Plug - p.dir, _ = filepath.Abs( "./plug/") + p.dir, _ = filepath.Abs("./plug/") p.LoadInternalPlugList() p.LoadExternalPlugList() @@ -60,16 +61,18 @@ func (p *Plug) LoadInternalPlugList() { list := make(map[string]PlugInterface) //Mysql - list["mysql"] = mysql.NewInstance() + list["mysql"] = mysql.NewInstance() //Mongodb - list["mongodb"] = mongodb.NewInstance() + list["mongodb"] = mongodb.NewInstance() //Redis - list["redis"] = redis.NewInstance() + list["redis"] = redis.NewInstance() //Http - list["http"] = hp.NewInstance() + list["http"] = hp.NewInstance() + + list["mssql"] = mssql.NewInstance() p.InternalPlugList = list } @@ -87,7 +90,7 @@ func (p *Plug) LoadExternalPlugList() { continue } - plug, err := plugin.Open(p.dir+"/"+fi.Name()) + plug, err := plugin.Open(p.dir + "/" + fi.Name()) if err != nil { panic(err) } @@ -113,12 +116,12 @@ func (p *Plug) LoadExternalPlugList() { } version := versionFunc.(func() string)() - p.ExternalPlugList[fi.Name()] = ExternalPlug { - ResolvePacket:ResolvePacketFunc.(func(net gopacket.Flow, transport gopacket.Flow, r io.Reader)), - SetFlag:setFlagFunc.(func([]string)), - BPFFilter:BPFFilterFunc.(func() string), - Version:version, - Name:fi.Name(), + p.ExternalPlugList[fi.Name()] = ExternalPlug{ + ResolvePacket: ResolvePacketFunc.(func(net gopacket.Flow, transport gopacket.Flow, r io.Reader)), + SetFlag: setFlagFunc.(func([]string)), + BPFFilter: BPFFilterFunc.(func() string), + Version: version, + Name: fi.Name(), } } } @@ -131,7 +134,7 @@ func (p *Plug) PrintList() { //Print Internal Plug for inPlugName, _ := range p.InternalPlugList { - fmt.Println("internal plug : "+inPlugName) + fmt.Println("internal plug : " + inPlugName) } //split @@ -139,24 +142,26 @@ func (p *Plug) PrintList() { //print External Plug for exPlugName, _ := range p.ExternalPlugList { - fmt.Println("external plug : "+exPlugName) + fmt.Println("external plug : " + exPlugName) } } func (p *Plug) SetOption(plugName string, plugParams []string) { + fmt.Println("internalPlug", plugName) + //Load Internal Plug if internalPlug, ok := p.InternalPlugList[plugName]; ok { p.ResolveStream = internalPlug.ResolveStream internalPlug.SetFlag(plugParams) - p.BPF = internalPlug.BPFFilter() + p.BPF = internalPlug.BPFFilter() return } //Load External Plug - plug, err := plugin.Open("./plug/"+ plugName) + plug, err := plugin.Open("./plug/" + plugName) if err != nil { panic(err) } @@ -174,5 +179,5 @@ func (p *Plug) SetOption(plugName string, plugParams []string) { } p.ResolveStream = resolvePacket.(func(net gopacket.Flow, transport gopacket.Flow, r io.Reader)) setFlag.(func([]string))(plugParams) - p.BPF = BPFFilter.(func()string)() -} \ No newline at end of file + p.BPF = BPFFilter.(func() string)() +} diff --git a/main.go b/main.go index 4dfee3a..a5e0ef9 100644 --- a/main.go +++ b/main.go @@ -7,4 +7,4 @@ import ( func main() { core := core.New() core.Run() -} \ No newline at end of file +} diff --git a/plugSrc/http/build/entry.go b/plugSrc/http/build/entry.go index 7682f04..da8901c 100644 --- a/plugSrc/http/build/entry.go +++ b/plugSrc/http/build/entry.go @@ -1,28 +1,29 @@ package build import ( - "github.com/google/gopacket" - "io" - "log" - "strconv" - "fmt" - "os" "bufio" + "fmt" + "io" "net/http" + "os" + "strconv" + "time" + + "github.com/google/gopacket" ) const ( - Port = 80 - Version = "0.1" + Port = 80 + Version = "0.1" ) const ( - CmdPort = "-p" + CmdPort = "-p" ) type H struct { - port int - version string + port int + version string } var hp *H @@ -30,8 +31,8 @@ var hp *H func NewInstance() *H { if hp == nil { hp = &H{ - port :Port, - version:Version, + port: Port, + version: Version, } } return hp @@ -58,39 +59,39 @@ func (m *H) ResolveStream(net, transport gopacket.Flow, buf io.Reader) { msg += req.Form.Encode() msg += "]" - log.Println(msg) - + fmt.Println(time.Now().Format("2006-01-02 15:04:05.000") + " | " + msg) + // log.Println() req.Body.Close() } } } func (m *H) BPFFilter() string { - return "tcp and port "+strconv.Itoa(m.port); + return "tcp and port " + strconv.Itoa(m.port) } func (m *H) Version() string { return Version } -func (m *H) SetFlag(flg []string) { +func (m *H) SetFlag(flg []string) { c := len(flg) if c == 0 { return } - if c >> 1 == 0 { + if c>>1 == 0 { fmt.Println("ERR : Http Number of parameters") os.Exit(1) } - for i:=0;i>1 == 0 { + fmt.Println("ERR : Mssql Number of parameters") + os.Exit(1) + } + + for i := 0; i < c; i = i + 2 { + key := flg[i] + val := flg[i+1] + + switch key { + case CmdPort: + port, err := strconv.Atoi(val) + m.port = port + if err != nil { + panic("ERR : port") + } + if port < 0 || port > 65535 { + panic("ERR : port(0-65535)") + } + break + default: + panic("ERR : mssql's params") + + } + + } +} + +// ResolveStream ... +func (m *Mssql) ResolveStream(net, transport gopacket.Flow, buf io.Reader) { + //uuid + uuid := fmt.Sprintf("%v:%v", net.FastHash(), transport.FastHash()) + + // log.Println(uuid) + + if _, ok := m.source[uuid]; !ok { + var newStream = &stream{ + packets: make(chan *packet, 100), + } + m.source[uuid] = newStream + go newStream.resolve() + } + + for { + + // log.Println("ssss") + + newPacket := m.newPacket(net, transport, buf) + if newPacket == nil { + return + } + m.source[uuid].packets <- newPacket + + } + // log.Println('ddd') +} + +func (m *Mssql) newPacket(net, transport gopacket.Flow, r io.Reader) *packet { + // read packet + var packet *packet + var err error + packet, err = readStream(r) + + //stream close + if err == io.EOF { + fmt.Println(net, transport, " close") + return nil + } else if err != nil { + fmt.Println("ERR : Unknown stream", net, transport, ":", err) + return nil + } + + //set flow direction + if transport.Src().String() == strconv.Itoa(m.port) { + packet.isClientFlow = false + } else { + packet.isClientFlow = true + } + return packet +} + +func (m *stream) resolve() { + for { + select { + case packet := <-m.packets: + if packet.isClientFlow { + m.resolveClientPacket(packet) + } else { + m.resolveServerPacket(packet) + } + } + } +} + +func readStream(r io.Reader) (*packet, error) { + + var buffer bytes.Buffer + + header := make([]byte, 8) + p := &packet{} + if _, err := io.ReadFull(r, header); err != nil { + return nil, err + } + + p.packetType = int(uint32(header[0])) + p.status = int(uint32(header[1])) + p.length = int(binary.BigEndian.Uint16(header[2:4])) + + if p.length > 0 { + io.CopyN(&buffer, r, int64(p.length-8)) + } + p.payload = buffer.Bytes() + return p, nil +} + +func ucs22str(s []byte) (string, error) { + if len(s)%2 != 0 { + return "", fmt.Errorf("Illegal UCS2 string length: %d", len(s)) + } + buf := make([]uint16, len(s)/2) + for i := 0; i < len(s); i += 2 { + buf[i/2] = binary.LittleEndian.Uint16(s[i:]) + } + return string(utf16.Decode(buf)), nil +} + +func (m *stream) resolveClientPacket(p *packet) { + + var msg string + + switch p.packetType { + case 1: + headerLength := int(binary.LittleEndian.Uint32(p.payload[0:4])) + // fmt.Printf("headers %x %d\n %x \n", p.payload[0:4], headerLength, p.payload) + if headerLength > 22 { + //not exists headers + msg = fmt.Sprintf("【query】 %s", string(p.payload)) + + } else { + //tds 7.2+ + msg = fmt.Sprintf("【query】 %s", string(p.payload[headerLength:])) + } + case 3: + // 4 byte + pos := 0 + headerLength := int(binary.LittleEndian.Uint32(p.payload[0:4])) + // fmt.Printf("rpc headers %x %d\n \n", p.payload[0:4], headerLength) + pos += headerLength + + //rpc name length + rpcLength := int(binary.LittleEndian.Uint16(p.payload[pos : pos+2])) + + rpcLength = rpcLength * 2 + + pos += 2 + + rpcName, _ := ucs22str(p.payload[pos : pos+rpcLength]) + + // fmt.Printf("rpc name %s %d %x", rpcName, rpcLength, p.payload[pos:pos+rpcLength]) + + pos += rpcLength + + if strings.Compare(rpcName, `sp_executesql`) != 0 { + return + } + //OPTIONS Flags 2byte + + pos += 2 + + //name length 1byte + nameLength := int(p.payload[pos]) + // fmt.Printf("parameter nameLength %d", nameLength) + + pos = pos + 1 + nameLength*2 + + //STATUS FLAGS 1byte + pos += 1 + + typeNvarchar := p.payload[pos] + // fmt.Printf("typeNvarchar %x ", typeNvarchar) + if typeNvarchar == 0xe7 { + pos += 7 + + //value + valueLength := int(binary.LittleEndian.Uint16(p.payload[pos+1 : pos+3])) + pos += 2 + + msg = fmt.Sprintf("【query】%s", string(p.payload[pos:pos+valueLength])) + + } + // ParameterData + + case 4: + msg = fmt.Sprintf("【query】 %s", "Tabular result") + + } + + fmt.Println(GetNowStr(true), msg) +} + +func (m *stream) resolveServerPacket(p *packet) { + + var msg string + switch p.packetType { + case 4: //response + rows, errMsg := parseToken(p.payload) + if rows == 0 && len(errMsg) != 0 { + msg = fmt.Sprintf("【Err】Effect Rows:%d, message: %s", rows, errMsg) + } else { + msg = fmt.Sprintf("【OK】Effect Rows:%d", rows) + } + } + + fmt.Println(GetNowStr(false), msg) +} diff --git a/plugSrc/mssql/build/token.go b/plugSrc/mssql/build/token.go new file mode 100644 index 0000000..dd47820 --- /dev/null +++ b/plugSrc/mssql/build/token.go @@ -0,0 +1,590 @@ +package build + +import ( + "encoding/binary" +) + +type token byte + +// token ids +const ( + tokenReturnStatus token = 121 // 0x79 + tokenColMetadata token = 129 // 0x81 + tokenOrder token = 169 // 0xA9 + tokenError token = 170 // 0xAA + tokenInfo token = 171 // 0xAB + tokenReturnValue token = 0xAC + tokenLoginAck token = 173 // 0xad + tokenRow token = 209 // 0xd1 + tokenNbcRow token = 210 // 0xd2 + tokenEnvChange token = 227 // 0xE3 + tokenSSPI token = 237 // 0xED + tokenDone token = 253 // 0xFD + tokenDoneProc token = 254 + tokenDoneInProc token = 255 +) + +// fixed-length data types +// http://msdn.microsoft.com/en-us/library/dd341171.aspx +const ( + typeNull = 0x1f + typeInt1 = 0x30 + typeBit = 0x32 + typeInt2 = 0x34 + typeInt4 = 0x38 + typeDateTim4 = 0x3a + typeFlt4 = 0x3b + typeMoney = 0x3c + typeDateTime = 0x3d + typeFlt8 = 0x3e + typeMoney4 = 0x7a + typeInt8 = 0x7f +) + +const _PLP_NULL = 0xFFFFFFFFFFFFFFFF +const _UNKNOWN_PLP_LEN = 0xFFFFFFFFFFFFFFFE +const _PLP_TERMINATOR = 0x00000000 + +// variable-length data types +// http://msdn.microsoft.com/en-us/library/dd358341.aspx +const ( + // byte len types + typeGuid = 0x24 + typeIntN = 0x26 + typeDecimal = 0x37 // legacy + typeNumeric = 0x3f // legacy + typeBitN = 0x68 + typeDecimalN = 0x6a + typeNumericN = 0x6c + typeFltN = 0x6d + typeMoneyN = 0x6e + typeDateTimeN = 0x6f + typeDateN = 0x28 + typeTimeN = 0x29 + typeDateTime2N = 0x2a + typeDateTimeOffsetN = 0x2b + typeChar = 0x2f // legacy + typeVarChar = 0x27 // legacy + typeBinary = 0x2d // legacy + typeVarBinary = 0x25 // legacy + + // short length types + typeBigVarBin = 0xa5 + typeBigVarChar = 0xa7 + typeBigBinary = 0xad + typeBigChar = 0xaf + typeNVarChar = 0xe7 + typeNChar = 0xef + typeXml = 0xf1 + typeUdt = 0xf0 + typeTvp = 0xf3 + + // long length types + typeText = 0x23 + typeImage = 0x22 + typeNText = 0x63 + typeVariant = 0x62 +) + +type columnStruct struct { + UserType uint32 + Flags uint16 + ColName string + Size int + TypeId int + Reader func(column *columnStruct, buf []byte) int +} + +func readTypeInfo(pos int, buf []byte, column *columnStruct) (count int) { + typeId := buf[pos] + + count = 1 + pos++ + column.TypeId = int(typeId) + // fmt.Printf("column TypeId %x %x\n", column.TypeId, buf) + + switch typeId { + case typeNull, typeInt1, typeBit, typeInt2, typeInt4, typeDateTim4, + typeFlt4, typeMoney, typeDateTime, typeFlt8, typeMoney4, typeInt8: + count += 0 + switch typeId { + case typeNull: + column.Size = 0 + case typeInt1, typeBit: + column.Size = 1 + case typeInt2: + column.Size = 2 + case typeInt4, typeDateTim4, typeFlt4, typeMoney4: + column.Size = 4 + case typeMoney, typeDateTime, typeFlt8, typeInt8: + column.Size = 8 + } + + column.Reader = readFixedType + // those are fixed length types + default: // all others are VARLENTYPE + count += readVarLen(int(typeId), pos, buf, column) + } + return count +} + +func readFixedType(column *columnStruct, buf []byte) int { + return column.Size +} + +func readByteLenType(column *columnStruct, buf []byte) int { + size := int(buf[0]) + return 1 + size +} + +// partially length prefixed stream +// http://msdn.microsoft.com/en-us/library/dd340469.aspx +func readPLPType(column *columnStruct, buf []byte) int { + size := binary.LittleEndian.Uint64(buf[0:8]) + valueLength := 0 + switch size { + case _PLP_NULL: + valueLength = 0 + case _UNKNOWN_PLP_LEN: + valueLength = 1000 + default: + valueLength = int(size) + } + return valueLength + 8 +} + +func readShortLenType(column *columnStruct, buf []byte) int { + size := int(binary.LittleEndian.Uint16(buf[0:2])) + + return 2 + size +} + +func readLongLenType(column *columnStruct, buf []byte) int { + count := 1 + textptrsize := int(buf[0]) //textptrsize + if textptrsize == 0 { + return 1 + } + + count = textptrsize + 8 + 1 + //timestamp 8 + + size := int(binary.LittleEndian.Uint32(buf[count : count+4])) + if size == -1 { + return count + 4 + } + return count + 4 + size + +} + +// reads variant value +// http://msdn.microsoft.com/en-us/library/dd303302.aspx +func readVariantType(column *columnStruct, buf []byte) int { + count := 0 + size := int(binary.LittleEndian.Uint32(buf[count : count+4])) + count = 4 + if size == 0 { + return count + } + vartype := int(buf[count]) + count += 1 + propbytes := int(buf[count]) + switch vartype { + case typeGuid: + + count = count + size - 2 - propbytes + case typeBit: + count += 1 + case typeInt1: + count += 1 + case typeInt2: + count += 2 + case typeInt4: + count += 4 + case typeInt8: + count += 8 + case typeDateTime: + count = count + size - 2 - propbytes + + case typeDateTim4: + count = count + size - 2 - propbytes + + case typeFlt4: + count = count + 4 + case typeFlt8: + count = count + 8 + case typeMoney4: + count = count + size - 2 - propbytes + + case typeMoney: + count = count + size - 2 - propbytes + + case typeDateN: + count = count + size - 2 - propbytes + + case typeTimeN: + count += 1 + count = count + size - 2 - propbytes + case typeDateTime2N: + count += 1 + count = count + size - 2 - propbytes + case typeDateTimeOffsetN: + count += 1 + count = count + size - 2 - propbytes + case typeBigVarBin, typeBigBinary: + count += 2 + count = count + size - 2 - propbytes + case typeDecimalN, typeNumericN: + count += 2 + count = count + size - 2 - propbytes + case typeBigVarChar, typeBigChar: + count += 5 + count += 2 // max length, ignoring + count = count + size - 2 - propbytes + + case typeNVarChar, typeNChar: + count += 5 + count += 2 // max length, ignoring + count = count + size - 2 - propbytes + default: + panic("Invalid variant typeid") + } + return count +} + +func readVarLen(typeId int, pos int, buf []byte, column *columnStruct) (count int) { + count = 0 + switch typeId { + case typeDateN: + column.Size = 3 + column.Reader = readByteLenType + case typeTimeN, typeDateTime2N, typeDateTimeOffsetN: + + pos += 1 //Scale + count += 1 + + scale := buf[pos] + + switch scale { + case 0, 1, 2: + column.Size = 3 + case 3, 4: + column.Size = 4 + case 5, 6, 7: + column.Size = 5 + } + + switch typeId { + case typeDateTime2N: + column.Size += 3 + case typeDateTimeOffsetN: + column.Size += 5 + } + + column.Reader = readByteLenType + + case typeGuid, typeIntN, typeDecimal, typeNumeric, + typeBitN, typeDecimalN, typeNumericN, typeFltN, + typeMoneyN, typeDateTimeN, typeChar, + typeVarChar, typeBinary, typeVarBinary: + // byle len types + + pos += 1 //byle len types + count += 1 + + column.Size = int(buf[pos]) //size + switch typeId { + case typeDecimal, typeNumeric, typeDecimalN, typeNumericN: + pos += 2 //byle len types + count += 2 + } + column.Reader = readByteLenType + case typeXml: + pos += 1 //byle len types + count += 1 + schemaPresent := buf[pos] + + if schemaPresent != 0 { + pos += 1 //byle len types + count += 1 + l := int(buf[pos]) // dbname + count += l + pos += l + + pos += 1 // owning schema + count += 1 + l = int(buf[pos]) // owning schema + count += l + pos += l + + // xml schema collection + pos += 1 + l = int(binary.LittleEndian.Uint16(buf[pos : pos+2])) + pos += 1 + count += 2 + pos += l * 2 + count += l * 2 + } + column.Reader = readPLPType + case typeUdt: + pos += 1 + l := int(binary.LittleEndian.Uint16(buf[pos : pos+2])) + pos += 1 + count += 2 + //ti.Size + column.Size = l + + //DBName + pos += 1 // owning schema + count += 1 + l = int(buf[pos]) + count += l + pos += l + + //SchemaName + pos += 1 // owning schema + count += 1 + l = int(buf[pos]) + count += l + pos += l + + //TypeName + pos += 1 // owning schema + count += 1 + l = int(buf[pos]) + count += l + pos += l + + //AssemblyQualifiedName + pos += 1 + l = int(binary.LittleEndian.Uint16(buf[pos : pos+2])) + pos += 1 + count += 2 + pos += l * 2 + count += l * 2 + + column.Reader = readPLPType + case typeBigVarBin, typeBigVarChar, typeBigBinary, typeBigChar, + typeNVarChar, typeNChar: + // short len types + pos += 1 + l := int(binary.LittleEndian.Uint16(buf[pos : pos+2])) + pos += 1 + count += 2 + + column.Size = l + + switch typeId { + case typeBigVarChar, typeBigChar, typeNVarChar, typeNChar: + pos += 5 + count += 5 + } + + if column.Size == 0xffff { + column.Reader = readPLPType + } else { + column.Reader = readShortLenType + } + + case typeText, typeImage, typeNText, typeVariant: + // LONGLEN_TYPE + + l := int(binary.LittleEndian.Uint16(buf[pos+1 : pos+5])) + column.Size = l + + pos += 4 + count += 4 + + switch typeId { + case typeText, typeNText: + pos += 6 + count += 6 + // ignore tablenames + numparts := int(buf[pos]) + for i := 0; i < numparts; i++ { + pos += 1 + l := int(binary.LittleEndian.Uint16(buf[pos : pos+2])) + pos += 1 + count += 2 + pos += l + count += l + } + column.Reader = readLongLenType + case typeImage: + // ignore tablenames + pos++ + count++ + numparts := int(buf[pos]) + for i := 0; i < numparts; i++ { + pos += 1 + l := int(binary.LittleEndian.Uint16(buf[pos : pos+2])) + pos += 1 + count += 2 + pos += l + count += l + } + column.Reader = readLongLenType + + case typeVariant: + column.Reader = readVariantType + + } + default: + count += 0 + } + return count +} + +func parseToken(buf []byte) (rowCount int, msg string) { + + var pos = 0 + length := 0 + rowCount = 0 + msg = "" + + var columns []columnStruct + + defer func() { + if r := recover(); r != nil { + msg = "parse tds result error" + } + }() + + currentBuf := buf[:] + + // fmt.Printf("buf len %x", currentBuf) + for { + + if len(currentBuf) == 0 { + break + } + + token := token(currentBuf[0]) + // fmt.Printf("item... %x %d\n", currentBuf[0], currentBuf[0]) + currentBuf = currentBuf[1:] + + switch token { + case tokenSSPI: + length = int(binary.LittleEndian.Uint16(currentBuf[0:2])) + currentBuf = currentBuf[length+2:] + case tokenReturnStatus: + currentBuf = currentBuf[4:] + + case tokenLoginAck: + length = int(binary.LittleEndian.Uint16(currentBuf[0:2])) + currentBuf = currentBuf[length+2:] + + case tokenOrder: + length = int(binary.LittleEndian.Uint16(currentBuf[0:2])) + //col ColIds + currentBuf = currentBuf[2+2*length:] //ColIds data + + case tokenDoneInProc: + currentBuf = currentBuf[4:] + rowCount = int(binary.LittleEndian.Uint64(currentBuf[0:8])) + currentBuf = currentBuf[8:] + case tokenDone, tokenDoneProc: + currentBuf = currentBuf[4:] + rowCount = int(binary.LittleEndian.Uint64(currentBuf[0:8])) + currentBuf = currentBuf[8:] + + case tokenError: + currentBuf = currentBuf[8:] //length2+Number4+State1+Class1 + //message length + msgLength := int(binary.LittleEndian.Uint16(currentBuf[0:2])) + currentBuf = currentBuf[2:] + msgLength = msgLength * 2 + + msg, _ = ucs22str(currentBuf[0:msgLength]) + return + case tokenColMetadata: + + //http://msdn.microsoft.com/en-us/library/dd357363.aspx + count := int(binary.LittleEndian.Uint16(currentBuf[0:2])) + currentBuf = currentBuf[2:] + + if count == 0xffff { + break + } + columns = make([]columnStruct, count) + + if count > 0 { + for i := 0; i < count; i++ { + + // fmt.Printf("colums %d %d", i, count) + column := &columns[i] + // x := pos + + currentBuf = currentBuf[6:] + + pos = readTypeInfo(0, currentBuf, column) + + currentBuf = currentBuf[pos:] + + //ColName + l := int(currentBuf[0]) + + currentBuf = currentBuf[1:] + //name + currentBuf = currentBuf[l*2:] + } + + // fmt.Printf("tokenRow %x\n", currentBuf) + + } + case tokenRow: + count := 0 + + for _, column := range columns { + + count = column.Reader(&column, currentBuf) + currentBuf = currentBuf[count:] + + } + case tokenNbcRow: + bitlen := (len(columns) + 7) / 8 + + pres := currentBuf[0:bitlen] + currentBuf = currentBuf[bitlen:] + count := 0 + + for i, column := range columns { + if pres[i/8]&(1<<(uint(i)%8)) != 0 { + continue + } + count = column.Reader(&column, currentBuf) + currentBuf = currentBuf[count:] + + // fmt.Printf("tokenNbcRow %d %x \n", i, currentBuf) + + } + case tokenEnvChange: + // http://msdn.microsoft.com/en-us/library/dd303449.aspx + length = int(binary.LittleEndian.Uint16(currentBuf[0:2])) + currentBuf = currentBuf[2+length:] + case tokenInfo: + // http://msdn.microsoft.com/en-us/library/dd304156.aspx + length = int(binary.LittleEndian.Uint16(currentBuf[0:2])) + currentBuf = currentBuf[2+length:] + case tokenReturnValue: + // https://msdn.microsoft.com/en-us/library/dd303881.aspx + currentBuf = currentBuf[2:] + nameLength := int(currentBuf[0]) + currentBuf = currentBuf[1:] + currentBuf = currentBuf[nameLength*2:] + currentBuf = currentBuf[7:] //1byte + 4 byte+2 byt + col := columnStruct{} + count := readTypeInfo(0, currentBuf, &col) + currentBuf = currentBuf[count:] //readTypeInfo + + count = col.Reader(&col, currentBuf) + currentBuf = currentBuf[count:] //column value + + default: + // fmt.Printf("tokenNbcRow %x \n", currentBuf[0]) + return rowCount, "parse result error" + } + + } + return rowCount, msg +} diff --git a/plugSrc/mssql/build/util.go b/plugSrc/mssql/build/util.go new file mode 100644 index 0000000..2a2b6dc --- /dev/null +++ b/plugSrc/mssql/build/util.go @@ -0,0 +1,14 @@ +package build + +import "time" + +func GetNowStr(isClient bool) string { + var msg string + msg += time.Now().Format("2006-01-02 15:04:05.000") + if isClient { + msg += "| cli -> ser |" + } else { + msg += "| ser -> cli |" + } + return msg +} diff --git a/plugSrc/mysql/build/entry.go b/plugSrc/mysql/build/entry.go index d710fca..2efae9b 100644 --- a/plugSrc/mysql/build/entry.go +++ b/plugSrc/mysql/build/entry.go @@ -1,30 +1,31 @@ package build import ( - "github.com/google/gopacket" - "io" "bytes" + "encoding/binary" "errors" + "fmt" + "io" "log" + "os" "strconv" + "strings" "sync" "time" - "fmt" - "encoding/binary" - "strings" - "os" + + "github.com/google/gopacket" ) const ( - Port = 3306 - Version = "0.1" - CmdPort = "-p" + Port = 3306 + Version = "0.1" + CmdPort = "-p" ) type Mysql struct { - port int - version string - source map[string]*stream + port int + version string + source map[string]*stream } type stream struct { @@ -34,20 +35,21 @@ type stream struct { type packet struct { isClientFlow bool - seq int - length int - payload []byte + seq int + length int + payload []byte } var mysql *Mysql var once sync.Once + func NewInstance() *Mysql { once.Do(func() { mysql = &Mysql{ - port :Port, - version:Version, - source: make(map[string]*stream), + port: Port, + version: Version, + source: make(map[string]*stream), } }) @@ -63,8 +65,8 @@ func (m *Mysql) ResolveStream(net, transport gopacket.Flow, buf io.Reader) { if _, ok := m.source[uuid]; !ok { var newStream = stream{ - packets:make(chan *packet, 100), - stmtMap:make(map[uint32]*Stmt), + packets: make(chan *packet, 100), + stmtMap: make(map[uint32]*Stmt), } m.source[uuid] = &newStream @@ -86,31 +88,31 @@ func (m *Mysql) ResolveStream(net, transport gopacket.Flow, buf io.Reader) { } func (m *Mysql) BPFFilter() string { - return "tcp and port "+strconv.Itoa(m.port); + return "tcp and port " + strconv.Itoa(m.port) } func (m *Mysql) Version() string { return Version } -func (m *Mysql) SetFlag(flg []string) { +func (m *Mysql) SetFlag(flg []string) { c := len(flg) if c == 0 { return } - if c >> 1 == 0 { + if c>>1 == 0 { fmt.Println("ERR : Mysql Number of parameters") os.Exit(1) } - for i:=0;i> 1 != 1 { + if c>>1 != 1 { panic("ERR : Redis num of params") } - for i:=0;i