mysql協議解析之握手協議(handshake)

要解析mysql協議須要瞭解一下知識和軟件

1、wireshark

wireshark 是一款網絡抓包工具,能夠讓咱們很方便的查看mysql協議。html

2、網絡字節序

網絡字節序分爲大端法和小端法,它們的區別能夠參考其它網站,在此不作敘述。mysql

3、golang

golang是一款很是讚的開發語言,學習成本比較低,併發性能好的特色golang

mysql 協議文檔詳見:(http://www.cnblogs.com/davygeek/p/5647175.html)sql

mysql 協議分爲報文頭和報文體 輸入圖片說明 輸入圖片說明服務器

這裏爲了簡單起見,沒有處理任何的異常狀況網絡

package main

import (
	"bytes"
	"fmt"
	"io"
	"net"
)

type Handshake struct {
	//1Byte 協議版本號
	protocol byte
	//n Byte 服務器版本信息(Null-Termimated-String)
	version string
	//4Byte服務器線程ID
	theadID int
	//8bytes 隨機挑戰數
	salt1 string
	//1Byte 填充值(0x00)
	fill uint8
	//2Byte 服務器權能標誌(低16位)
	capabilitiesLow int
	//1Byte字符編碼
	charset uint8
	//2Byte服務器狀態
	status int
	//2Byte 服務器權能標誌(高16位)
	capabilitiesHigh int
	//隨機挑戰數(12位)
	salt2 string
	//
	pluginName string
}

const (
	ipAddr = "127.0.0.1"
	port   = "3306"
)

func main() {

	conn, _ := net.Dial("tcp", fmt.Sprintf("%s:%s", ipAddr, port))
	defer conn.Close()
	//讀取消息體
	body, _ := ReadPackge(conn)

	r := bytes.NewReader(body)
	//跳過4Byte消息頭
	SkipHeaderPackage(r)
	//讀取1Byte協議版本號
	protoVersion, _ := r.ReadByte()

	//n Byte服務版本信息
	serverVersion := ReadNullTerminatedString(r)

	//4 byte 服務線程ID
	theadIDBuf := make([]byte, 4)
	r.Read(theadIDBuf)
	theadId := int(uint32(theadIDBuf[0]) | uint32(theadIDBuf[1])<<8 | uint32(theadIDBuf[2])<<16 | uint32(theadIDBuf[3])<<24)

	//8 byte 隨機挑戰數
	saltBuf := make([]byte, 8)
	salt, _ := r.Read(saltBuf)

	//跳過 1 byte填充值
	r.Seek(1, io.SeekCurrent)

	//讀取 2 byte 服務器權能標識(低16位)
	capabilitiesLow1Buf := make([]byte, 2)
	r.Read(capabilitiesLow1Buf)
	capabilitiesLow := int(uint32(capabilitiesLow1Buf[0]) | uint32(capabilitiesLow1Buf[1])<<8)
	//讀取1 byte 字符編碼
	chartset, _ := r.ReadByte()

	//讀取2 byte 服務器狀態
	serverStatusBuf := make([]byte, 2)
	r.Read(serverStatusBuf)
	serverStatus := int(uint32(serverStatusBuf[0]) | uint32(serverStatusBuf[1]))

	//讀取服務器權能標誌(高16位)
	capabilitiesHigh2Buf := make([]byte, 2)
	r.Read(capabilitiesHigh2Buf)
	capabilitiesHigh := int(uint32(capabilitiesHigh2Buf[0]) | uint32(capabilitiesHigh2Buf[1])<<8)
	//跳過 1byte 未使用的挑戰長度
	r.Seek(1, io.SeekCurrent)
	//跳過10 byte 填充值
	r.Seek(10, io.SeekCurrent)

	//讀取12位挑戰隨機數
	salt2 := ReadNullTerminatedString(r)

	//讀取密碼認證插件
	pluginName := ReadNullTerminatedString(r)

	//handshake
	handshake := Handshake{
		protocol:         protoVersion,
		version:          serverVersion,
		theadID:          theadId,
		salt1:            string(salt),
		capabilitiesLow:  capabilitiesLow,
		charset:          chartset,
		status:           serverStatus,
		capabilitiesHigh: capabilitiesHigh,
		salt2:            salt2,
		pluginName:       pluginName,
	}
    //打印輸出
	fmt.Println(handshake)

}

//讀取已0x00結尾的字符串
func ReadNullTerminatedString(r *bytes.Reader) string {
	var str []byte
	for {
		b, _ := r.ReadByte()
		if b == 0x00 {
			return string(str)
		} else {
			str = append(str, b)
		}
	}

}

//跳過消息頭
func SkipHeaderPackage(r *bytes.Reader) error {
	if _, err := r.Seek(4, io.SeekStart); err != nil {
		return err
	}
	return nil

}

//讀取數據包
func ReadPackge(conn net.Conn) ([]byte, error) {

	//讀取4byte消息頭
	header := []byte{0, 0, 0, 0}

	if _, err := io.ReadFull(conn, header); err != nil {
		return nil, err
	}
	//獲取3byte消息長度
	bodyLen := int(uint32(header[0]) | uint32(header[1]<<8) | uint32(header[2]<<16))

	body := make([]byte, bodyLen)
	//讀取消息體
	n, err := io.ReadFull(conn, body)
	if err != nil {
		return nil, err
	}

	return append(header, body[0:n]...), nil

}
相關文章
相關標籤/搜索