golang mysql 诊断之旅(2000万开房数据被曝光引发的血案)

golang mysql 诊断之旅(2000万开房数据被曝光引发的血案),第1张

概述最近由于某某漏洞原因,2000万开房数据被曝光,数据是csv格式,打开慢的要死,于是想把这2000w的开房数据导入mysql,然后用go写个简单的查询工具。 悲剧开始了: 第一步,下载 mysql模块,go get github.com/go-sql-driver/mysql, 第二步,写个小例子测试下 package mainimport ( "database/sql" //这包一定要引

最近由于某某漏洞原因,2000万开房数据被曝光,数据是csv格式,打开慢的要死,于是想把这2000w的开房数据导入MysqL,然后用go写个简单的查询工具。


悲剧开始了:

第一步,下载 MysqL模块,go get github.com/go-sql-driver/MysqL,

第二步,写个小例子测试下

package mainimport (	"database/sql" //这包一定要引用	"enCoding/Json"	"fmt"                              //这个前面一章讲过	_ "github.com/go-sql-driver/MysqL" //这就是刚才下载的包)// 定义一个结构体,需要大写开头哦,字段名也需要大写开头哦,否则Json模块会识别不了// 结构体成员仅大写开头外界才能访问type User struct {	User     string `Json:"user"`	Password string `Json:"password"`	Host     string `Json:"host"`}// 一如既往的main方法func main() {	// 格式有点怪,@tcp 是指网络协议(难道支持udp?),然后是域名和端口	db,e := sql.Open("MysqL","root:@tcp(192.168.7.15:3306)/MysqL?charset=utf8")	if e != nil { //如果连接出错,e将不是nil的		print("ERROR?")		return	}	defer db.Close()	// 提醒一句,运行到这里,并不代表数据库连接是完全OK的,因为发送第一条sql才会校验密码 汗~!	rows,e := db.query("select user,password,host from MysqL.user")	if e != nil {		fmt.Printf("query error!!%v\n",e)		return	}	if rows == nil {		print("Rows is nil")		return	}	fmt.Println("DB rows.Next")	for rows.Next() { //跟java的ResultSet一样,需要先next读取		user := new(User)		// rows貌似只支持Scan方法 继续汗~! 当然,可以通过GetColumns()来得到字段顺序		row_err := rows.Scan(&user.User,&user.Password,&user.Host)		if row_err != nil {			print("Row error!!")			return		}		b,_ := Json.Marshal(user)		fmt.Println(string(b)) // 这里没有判断错误,呵呵,一般都不会有错吧	}	fmt.Println("Done")}

结果一直报错:

	panic: runtime error: index out of range		goroutine 1 [running]:	github.com/go-sql-driver/MysqL.readLengthEncodedInteger(0x10fb0037,0x1,0xfc9,0x0,...)		E:/go/src/github.com/go-sql-driver/MysqL/utils.go:406 +0x3e8	github.com/go-sql-driver/MysqL.skipLengthEnodedString(0x10fb0037,0x2,...)		E:/go/src/github.com/go-sql-driver/MysqL/utils.go:366 +0x38	github.com/go-sql-driver/MysqL.(*MysqLConn).readColumns(0x10f88230,0x10f86500,...)		E:/go/src/github.com/go-sql-driver/MysqL/packets.go:482 +0x389	github.com/go-sql-driver/MysqL.(*MysqLConn).getSystemVar(0x10f88230,0x530b88,0x12,...)		E:/go/src/github.com/go-sql-driver/MysqL/connection.go:228 +0x118	github.com/go-sql-driver/MysqL.(*MysqLDriver).Open(0x5f0bf4,0x547aa8,0x2f,0x10f9f900,...)		E:/go/src/github.com/go-sql-driver/MysqL/driver.go:70 +0x2de	database/sql.(*DB).conn(0x10f85e40,0x10f50228,0xff014c,0x5)		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/database/sql/sql.go:484 +0x15e	database/sql.(*DB).query(0x10f85e40,0x527b68,0x8,...)		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/database/sql/sql.go:708 +0x58	database/sql.(*DB).query(0x10f85e40,...)		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/database/sql/sql.go:699 +0x6b	main.main()		E:/go/src/testMysqL/testMysqL.go:54 +0x89		goroutine 3 [syscall]:	syscall.Syscall6(0x7c80a7bd,0x5,0xf70,0x10f86420,0x10f50280,...)		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/runtime/zsyscall_windows_windows_386.c:97 +0x49	syscall.GetQueuedCompletionStatus(0xf70,0x10f50278,0xffffffff,...)		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/syscall/zsyscall_windows_386.go:507 +0x7e	net.(*resultSrv).Run(0x10f50260)		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/net/fd_windows.go:150 +0x11a	created by net.startServer		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/net/fd_windows.go:285 +0xde		goroutine 4 [select]:	net.(*ioSrv).ProcessRemoteIO(0x10f50268)		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/net/fd_windows.go:183 +0x171	created by net.startServer		C:/Users/adminI~1/AppData/Local/Temp/2/bindist465310315/go/src/pkg/net/fd_windows.go:293 +0x163	exit status 2

刚开始怀疑windows没有装MysqL驱动,可是兴趣来了想分析下堆栈信息,正好学点新东西,

log大法,跟踪readLengthEncodedInteger,并加入如下调试代码,打印b[]byte的内存信息,发现b[0] = 0xfe,但是后面却没有数据了,所以造成了数组b的index溢出,完整代码如下:

func readLengthEncodedInteger(b []byte) (num uint64,isNull bool,n int) {	fmt.Println(b)	fmt.Printf("0x%02x\n",b[0])	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])<<54		n = 9		return	}	// 0-250: value of first byte	num = uint64(b[0])	n = 1	return}

继续跟踪发现:package.go的readColumns中有一个很奇怪的退出,但是没有这个0xfe的处理,而且奇怪的是for循环只有这里可以正常return。。。
func (mc *MysqLConn) readColumns(count int) (columns []MysqLFIEld,err error) {	var data []byte	var i,pos,n int	var name []byte	columns = make([]MysqLFIEld,count)	fmt.Println("count:")	fmt.Println(count)	for {		data,err = mc.readPacket()		if err != nil {			return		}		// EOF Packet		if data[0] == IEOF && len(data) == 5 {			if i != count {				err = fmt.Errorf("ColumnsCount mismatch n:%d len:%d",count,len(columns))			}			return		}


不太甘心,Google读MysqL的protocol http://dev.mysql.com/doc/internals/en/com-field-list-response.html

发现重要说明,这个Column的包后面有一个EOF_Packet,也就是上面的0xfe,于是怀疑这是go MysqL driver的一个BUG

15.6.5.1. COM_FIELD_List responseThe response to a COM_FIELD_List can either be aa ERR_Packet orone or more Column DeFinition packets and a closing EOF_Packet

于是到go-sql-driver的官网https://github.com/go-sql-driver/mysql/blob/master/packets.go,想pull->提交这个BUG的patch,结果。。。

我擦!!这个issue已经在github上修改了,如下:

           // EOF Packet                if data[0] == IEOF && (len(data) == 5 || len(data) == 1) {                        if i == count {                                return columns,nil                        }                        return nil,fmt.Errorf("ColumnsCount mismatch n:%d len:%d",len(columns))                }

顿时崩溃啊,猜想go get下来的代码是旧版本的,看README.md

**Current tagged Release:** June 03,2013 (Version 1.0.1)

再看github上

**Current tagged Release:** May 14,2013 (Version 1.0)

疑问来了,虽然github库上的release版本还是旧的,但是go get下来的新版本存在这个问题!!!???


解决方案:唯一的方式就是取一个github上的版本git clonehttps://github.com/go-sql-driver/MysqL.git,不要用go get的版本。


最后,哪位大神解释下 go get和git clone下来的版本为什么不一样??


补充:

1.@ASTA谢: 因为你之前安装过github.com/go-sql-driver/MysqL,所以你go get的时候不会更新,你必须使用go get -u更新,而git clone是最新版本的更新(2013-10-24 16:23:57),这个go get -u 是更新,但不是问题所在,@jimmykuu:默认是去获取tag为go1的代码,果然如jimmykuu所说:我查看tag为go1的代码:https://github.com/go-sql-driver/mysql/tree/go1,和go get下来的相同,而且golang上有说明。。。"When checking out or updating a package,get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1",get searches for a branch or tag named "go1". If no such version exists it retrIEves the most recent version of the package."

所以我的结论就是go-sql-driver中的go1 tag充满BUG,不负责任!

2. 刚才看了下,之所以其他人没有碰到这个问题,是因为我正好用的古老版本MysqL4.1 ,补丁在这fix crash when connect to MysqL4.1:https://github.com/go-sql-driver/mysql/commit/4a178617b97609ebd4d4a0ae5791225540c1bb26#diff-2357b8494bbd2f27c09e61fc8ef5f092


}
//EOFPacket
-ifdata[0]==IEOF&&len(data)==5{
+ifdata[0]==IEOF&&(len(data)==5||len(data)==1){
ifi!=count{
err=fmt.Errorf("ColumnsCountmismatchn:%dlen:%d",len(columns))
}

3.用git clone的新版本替换后,切记删掉pkg下生成的MysqL.a文件!!

总结

以上是内存溢出为你收集整理的golang mysql 诊断之旅(2000万开房数据被曝光引发的血案)全部内容,希望文章能够帮你解决golang mysql 诊断之旅(2000万开房数据被曝光引发的血案)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1292318.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存