Repository: microsoft/go-mssqldb
Pull request #77 (2023-01-06 14:34:37)

add driver name and version to TDS packets
With this change the SQL server `process_login_finish` extended event will have the driver name and version in it.
I allotted 8 bits for major, 16 bits for minor, and 8 bits for revision. 


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..160ca0d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Changelog
+
+## 0.20.0
+
+### Features
+
+* Add driver version and name to TDS login packets
+
+### Bug fixes
+
+* Added checks while reading prelogin for invalid data ([#64](https://github.com/microsoft/go-mssqldb/issues/64))([86ecefd8b](https://github.com/microsoft/go-mssqldb/commit/86ecefd8b57683aeb5ad9328066ee73fbccd62f5))
\ No newline at end of file
diff --git a/tds.go b/tds.go
index cc27a8f..b01220e 100644
--- a/tds.go
+++ b/tds.go
@@ -936,9 +936,10 @@ func preparePreloginFields(p msdsn.Config, fe *featureExtFedAuth) map[uint8][]by
 	case msdsn.EncryptionOff:
 		encrypt = encryptOff
 	}
-
+	v := getDriverVersion(driverVersion)
 	fields := map[uint8][]byte{
-		preloginVERSION:    {0, 0, 0, 0, 0, 0},
+		// 4 bytes for version and 2 bytes for minor version
+		preloginVERSION:    {byte(v), byte(v >> 8), byte(v >> 16), byte(v >> 24), 0, 0},
 		preloginENCRYPTION: {encrypt},
 		preloginINSTOPT:    instance_buf,
 		preloginTHREADID:   {0, 0, 0, 0},
@@ -992,15 +993,17 @@ func prepareLogin(ctx context.Context, c *Connector, p msdsn.Config, logger Cont
 		serverName = p.Host
 	}
 	l = &login{
-		TDSVersion:   verTDS74,
-		PacketSize:   packetSize,
-		Database:     p.Database,
-		OptionFlags2: fODBC, // to get unlimited TEXTSIZE
-		OptionFlags1: fUseDB | fSetLang,
-		HostName:     p.Workstation,
-		ServerName:   serverName,
-		AppName:      p.AppName,
-		TypeFlags:    typeFlags,
+		TDSVersion:    verTDS74,
+		PacketSize:    packetSize,
+		Database:      p.Database,
+		OptionFlags2:  fODBC, // to get unlimited TEXTSIZE
+		OptionFlags1:  fUseDB | fSetLang,
+		HostName:      p.Workstation,
+		ServerName:    serverName,
+		AppName:       p.AppName,
+		TypeFlags:     typeFlags,
+		CtlIntName:    "go-mssqldb",
+		ClientProgVer: getDriverVersion(driverVersion),
 	}
 	switch {
 	case fe.FedAuthLibrary == FedAuthLibrarySecurityToken:
diff --git a/version.go b/version.go
new file mode 100644
index 0000000..ee019c0
--- /dev/null
+++ b/version.go
@@ -0,0 +1,15 @@
+package mssql
+
+import "fmt"
+
+// Update this variable with the release tag before pushing the tag
+// This value is written to the prelogin and login7 packets during a new connection
+const driverVersion = "v0.20.0"
+
+func getDriverVersion(ver string) uint32 {
+	var majorVersion uint32
+	var minorVersion uint32
+	var rev uint32
+	_, _ = fmt.Sscanf(ver, "v%d.%d.%d", &majorVersion, &minorVersion, &rev)
+	return (majorVersion << 24) | (minorVersion << 16) | rev
+}


Test patch:
diff --git a/tds_login_test.go b/tds_login_test.go
index ab5e00c..70a6b29 100644
--- a/tds_login_test.go
+++ b/tds_login_test.go
@@ -61,7 +61,7 @@ func testLoginSequenceServer(result chan error, conn net.Conn, expectedPackets,
 
 		for b := 0; b < len(expectedBytes) && err == nil; {
 			n, err := conn.Read(packet)
-
+			p := hex.EncodeToString(packet[:n])
 			// Ignore EOF: ErrPipeClosed is the real signal
 			if err == io.EOF {
 				err = nil
@@ -82,8 +82,8 @@ func testLoginSequenceServer(result chan error, conn net.Conn, expectedPackets,
 					if bi < n {
 						suffix = fmt.Sprintf("%s Next byte:%02X", suffix, packet[bi+1])
 					}
-					err = fmt.Errorf("Client sent unexpected byte %02X != %02X at offset %d of packet %d. %s",
-						packet[bi], expectedBytes[bi+b], bi+b, i, suffix)
+					err = fmt.Errorf("Client sent unexpected byte %02X != %02X at offset %d of packet %d. %s\nActual:%s\nExpected:%s",
+						packet[bi], expectedBytes[bi+b], bi+b, i, suffix, p, expectedHex)
 
 					result <- err
 					return
@@ -126,24 +126,25 @@ func TestLoginWithSQLServerAuth(t *testing.T) {
 	tl := testLogger{t: t}
 	defer tl.StopLogging()
 	SetLogger(&tl)
-
+	v := versionToHexString(getDriverVersion(driverVersion))
 	mock := NewMockTransportDialer(
 		[]string{
-			"  12 01 00 2f 00 00 01 00  00 00 1a 00 06 01 00 20\n" +
-				"00 01 02 00 21 00 01 03  00 22 00 04 04 00 26 00\n" +
-				"01 ff 00 00 00 00 00 00  00 00 00 00 00 00 00\n",
-			"  10 01 00 b2 00 00 01 00  aa 00 00 00 04 00 00 74\n" +
-				"00 10 00 00 00 00 00 00  00 00 00 00 00 00 00 00\n" +
-				"A0 02 00 00 00 00 00 00  00 00 00 00 5e 00 09 00\n" +
-				"70 00 04 00 78 00 06 00  84 00 0a 00 98 00 09 00\n" +
-				"00 00 00 00 aa 00 00 00  aa 00 00 00 aa 00 00 00\n" +
-				"00 00 00 00 00 00 aa 00  00 00 aa 00 00 00 aa 00\n" +
-				"00 00 00 00 00 00 6c 00  6f 00 63 00 61 00 6c 00\n" +
-				"68 00 6f 00 73 00 74 00  74 00 65 00 73 00 74 00\n" +
-				"92 a5 f3 a5 93 a5 82 a5  f3 a5 e2 a5 67 00 6f 00\n" +
-				"2d 00 6d 00 73 00 73 00  71 00 6c 00 64 00 62 00\n" +
-				"6c 00 6f 00 63 00 61 00  6c 00 68 00 6f 00 73 00\n" +
-				"74 00\n",
+			fmt.Sprintf("12 01 00 2f 00 00 01 00  00 00 1a 00 06 01 00 20\n"+
+				"00 01 02 00 21 00 01 03  00 22 00 04 04 00 26 00\n"+
+				"01 ff %s             00 00  00 00 00 00 00 00 00\n", v),
+			fmt.Sprintf("10 01 00 c6 00 00 01 00  be 00 00 00 04 00 00 74\n"+
+				"00 10 00 00 %s           00 00 00 00 00 00 00 00\n"+
+				"A0 02 00 00 00 00 00 00  00 00 00 00 5e 00 09 00\n"+
+				"70 00 04 00 78 00 06 00  84 00 0a 00 98 00 09 00\n"+
+				"00 00 00 00 aa 00 0a 00  be 00 00 00 be 00 00 00\n"+
+				"00 00 00 00 00 00 be 00  00 00 be 00 00 00 be 00\n"+
+				"00 00 00 00 00 00 6c 00  6f 00 63 00 61 00 6c 00\n"+
+				"68 00 6f 00 73 00 74 00  74 00 65 00 73 00 74 00\n"+
+				"92 a5 f3 a5 93 a5 82 a5  f3 a5 e2 a5 67 00 6f 00\n"+
+				"2d 00 6d 00 73 00 73 00  71 00 6c 00 64 00 62 00\n"+
+				"6c 00 6f 00 63 00 61 00  6c 00 68 00 6f 00 73 00\n"+
+				"74 00 67 00 6f 00 2d 00  6d 00 73 00 73 00 71 00\n"+
+				"6c 00 64 00 62 00\n", v),
 		},
 		[]string{
 			"  04 01 00 20  00 00 01 00   00 00 10 00  06 01 00 16\n" +
@@ -186,25 +187,26 @@ func TestLoginWithSecurityTokenAuth(t *testing.T) {
 	tl := testLogger{t: t}
 	defer tl.StopLogging()
 	SetLogger(&tl)
-
+	v := versionToHexString(getDriverVersion(driverVersion))
 	mock := NewMockTransportDialer(
 		[]string{
-			"  12 01 00 35 00 00 01 00  00 00 1F 00 06 01 00 25\n" +
-				"00 01 02 00 26 00 01 03  00 27 00 04 04 00 2B 00\n" +
-				"01 06 00 2c 00 01 ff 00  00 00 00 00 00 00 00 00\n" +
-				"00 00 00 00 01\n",
-			"  10 01 00 BB 00 00 01 00  B3 00 00 00 04 00 00 74\n" +
-				"00 10 00 00 00 00 00 00  00 00 00 00 00 00 00 00\n" +
-				"A0 02 00 10 00 00 00 00  00 00 00 00 5E 00 09 00\n" +
-				"70 00 00 00 70 00 00 00  70 00 0A 00 84 00 09 00\n" +
-				"96 00 04 00 96 00 00 00  96 00 00 00 96 00 00 00\n" +
-				"00 00 00 00 00 00 96 00  00 00 96 00 00 00 96 00\n" +
-				"00 00 00 00 00 00 6C 00  6F 00 63 00 61 00 6C 00\n" +
-				"68 00 6F 00 73 00 74 00  67 00 6F 00 2D 00 6D 00\n" +
-				"73 00 73 00 71 00 6C 00  64 00 62 00 6C 00 6F 00\n" +
-				"63 00 61 00 6C 00 68 00  6F 00 73 00 74 00 9A 00\n" +
-				"00 00 02 13 00 00 00 03  0E 00 00 00 3C 00 74 00\n" +
-				"6F 00 6B 00 65 00 6E 00  3E 00 FF\n",
+			fmt.Sprintf("12 01 00 35 00 00 01 00  00 00 1F 00 06 01 00 25\n"+
+				"00 01 02 00 26 00 01 03  00 27 00 04 04 00 2B 00\n"+
+				"01 06 00 2c 00 01 ff %s           00 00 00 00 00\n"+
+				"00 00 00 00 01\n", v),
+			fmt.Sprintf("10 01 00 CF 00 00 01 00  C7 00 00 00 04 00 00 74\n"+
+				"00 10 00 00 %s           00 00 00 00 00 00 00 00\n"+
+				"A0 02 00 10 00 00 00 00  00 00 00 00 5E 00 09 00\n"+
+				"70 00 00 00 70 00 00 00  70 00 0A 00 84 00 09 00\n"+
+				"AA 00 04 00 96 00 0A 00  AA 00 00 00 AA 00 00 00\n"+
+				"00 00 00 00 00 00 AA 00  00 00 AA 00 00 00 AA 00\n"+
+				"00 00 00 00 00 00 6C 00  6F 00 63 00 61 00 6C 00\n"+
+				"68 00 6F 00 73 00 74 00  67 00 6F 00 2D 00 6D 00\n"+
+				"73 00 73 00 71 00 6C 00  64 00 62 00 6C 00 6F 00\n"+
+				"63 00 61 00 6C 00 68 00  6F 00 73 00 74 00 67 00\n"+
+				"6F 00 2D 00 6D 00 73 00  73 00 71 00 6C 00 64 00\n"+
+				"62 00 AE 00 00 00 02 13  00 00 00 03 0E 00 00 00\n"+
+				"3C 00 74 00 6F 00 6B 00  65 00 6E 00 3E 00 FF\n", v),
 		},
 		[]string{
 			"  04 01 00 20  00 00 01 00   00 00 10 00  06 01 00 16\n" +
@@ -249,24 +251,25 @@ func TestLoginWithADALUsernamePasswordAuth(t *testing.T) {
 	tl := testLogger{t: t}
 	defer tl.StopLogging()
 	SetLogger(&tl)
-
+	v := versionToHexString(getDriverVersion(driverVersion))
 	mock := NewMockTransportDialer(
 		[]string{
-			"  12 01 00 35 00 00 01 00  00 00 1F 00 06 01 00 25\n" +
-				"00 01 02 00 26 00 01 03  00 27 00 04 04 00 2B 00\n" +
-				"01 06 00 2C 00 01 ff 00  00 00 00 00 00 00 00 00\n" +
-				"00 00 00 00 01\n",
-			"  10 01 00 aa 00 00 01 00  a2 00 00 00 04 00 00 74\n" +
-				"00 10 00 00 00 00 00 00  00 00 00 00 00 00 00 00\n" +
-				"A0 02 00 10 00 00 00 00  00 00 00 00 5e 00 09 00\n" +
-				"70 00 00 00 70 00 00 00  70 00 0a 00 84 00 09 00\n" +
-				"96 00 04 00 96 00 00 00  96 00 00 00 96 00 00 00\n" +
-				"00 00 00 00 00 00 96 00  00 00 96 00 00 00 96 00\n" +
-				"00 00 00 00 00 00 6c 00  6f 00 63 00 61 00 6c 00\n" +
-				"68 00 6f 00 73 00 74 00  67 00 6f 00 2d 00 6d 00\n" +
-				"73 00 73 00 71 00 6c 00  64 00 62 00 6c 00 6f 00\n" +
-				"63 00 61 00 6c 00 68 00  6f 00 73 00 74 00 9a 00\n" +
-				"00 00 02 02 00 00 00 05  01 ff\n",
+			fmt.Sprintf("12 01 00 35 00 00 01 00  00 00 1F 00 06 01 00 25\n"+
+				"00 01 02 00 26 00 01 03  00 27 00 04 04 00 2B 00\n"+
+				"01 06 00 2C 00 01 ff %s  00 00 00 00 00\n"+
+				"00 00 00 00 01\n", v),
+			fmt.Sprintf("10 01 00 BE 00 00 01 00  b6 00 00 00 04 00 00 74\n"+
+				"00 10 00 00 %s           00 00 00 00 00 00 00 00\n"+
+				"A0 02 00 10 00 00 00 00  00 00 00 00 5e 00 09 00\n"+
+				"70 00 00 00 70 00 00 00  70 00 0a 00 84 00 09 00\n"+
+				"AA 00 04 00 96 00 0A 00  AA 00 00 00 AA 00 00 00\n"+
+				"00 00 00 00 00 00 AA 00  00 00 AA 00 00 00 AA 00\n"+
+				"00 00 00 00 00 00 6c 00  6f 00 63 00 61 00 6c 00\n"+
+				"68 00 6f 00 73 00 74 00  67 00 6f 00 2d 00 6d 00\n"+
+				"73 00 73 00 71 00 6c 00  64 00 62 00 6c 00 6f 00\n"+
+				"63 00 61 00 6c 00 68 00  6f 00 73 00 74 00 67 00\n"+
+				"6f 00 2d 00 6d 00 73 00  73 00 71 00 6c 00 64 00\n"+
+				"62 00 AE 00 00 00 02 02  00 00  00 05 01 ff\n", v),
 			"  08 01 00 1e 00 00 01 00  12 00 00 00 0e 00 00 00\n" +
 				"3c 00 74 00 6f 00 6b 00  65 00 6e 00 3e 00\n",
 		},
@@ -324,23 +327,25 @@ func TestLoginWithADALManagedIdentityAuth(t *testing.T) {
 	defer tl.StopLogging()
 	SetLogger(&tl)
 
+	v := versionToHexString(getDriverVersion(driverVersion))
 	mock := NewMockTransportDialer(
 		[]string{
-			"  12 01 00 35 00 00 01 00  00 00 1F 00 06 01 00 25\n" +
-				"00 01 02 00 26 00 01 03  00 27 00 04 04 00 2B 00\n" +
-				"01 06 00 2C 00 01 ff 00  00 00 00 00 00 00 00 00\n" +
-				"00 00 00 00 01\n",
-			"  10 01 00 aa 00 00 01 00  a2 00 00 00 04 00 00 74\n" +
-				"00 10 00 00 00 00 00 00  00 00 00 00 00 00 00 00\n" +
-				"A0 02 00 10 00 00 00 00  00 00 00 00 5e 00 09 00\n" +
-				"70 00 00 00 70 00 00 00  70 00 0a 00 84 00 09 00\n" +
-				"96 00 04 00 96 00 00 00  96 00 00 00 96 00 00 00\n" +
-				"00 00 00 00 00 00 96 00  00 00 96 00 00 00 96 00\n" +
-				"00 00 00 00 00 00 6c 00  6f 00 63 00 61 00 6c 00\n" +
-				"68 00 6f 00 73 00 74 00  67 00 6f 00 2d 00 6d 00\n" +
-				"73 00 73 00 71 00 6c 00  64 00 62 00 6c 00 6f 00\n" +
-				"63 00 61 00 6c 00 68 00  6f 00 73 00 74 00 9a 00\n" +
-				"00 00 02 02 00 00 00 05  03 ff\n",
+			fmt.Sprintf("12 01 00 35 00 00 01 00  00 00 1F 00 06 01 00 25\n"+
+				"00 01 02 00 26 00 01 03  00 27 00 04 04 00 2B 00\n"+
+				"01 06 00 2C 00 01 ff %s           00 00 00 00 00\n"+
+				"00 00 00 00 01\n", v),
+			fmt.Sprintf("10 01 00 be 00 00 01 00  b6 00 00 00 04 00 00 74\n"+
+				"00 10 00 00 %s           00 00 00 00 00 00 00 00\n"+
+				"A0 02 00 10 00 00 00 00  00 00 00 00 5e 00 09 00\n"+
+				"70 00 00 00 70 00 00 00  70 00 0a 00 84 00 09 00\n"+
+				"AA 00 04 00 96 00 0A 00  AA 00 00 00 AA 00 00 00\n"+
+				"00 00 00 00 00 00 AA 00  00 00 AA 00 00 00 AA 00\n"+
+				"00 00 00 00 00 00 6c 00  6f 00 63 00 61 00 6c 00\n"+
+				"68 00 6f 00 73 00 74 00  67 00 6f 00 2d 00 6d 00\n"+
+				"73 00 73 00 71 00 6c 00  64 00 62 00 6c 00 6f 00\n"+
+				"63 00 61 00 6c 00 68 00  6f 00 73 00 74 00 67 00\n"+
+				"6f 00 2d 00 6d 00 73 00  73 00 71 00 6c 00 64 00\n"+
+				"62 00 AE 00 00 00 02 02  00 00 00 05 03 ff\n", v),
 			"  08 01 00 1e 00 00 01 00  12 00 00 00 0e 00 00 00\n" +
 				"3c 00 74 00 6f 00 6b 00  65 00 6e 00 3e 00\n",
 		},
diff --git a/tds_test.go b/tds_test.go
index dc90835..ba0a864 100644
--- a/tds_test.go
+++ b/tds_test.go
@@ -5,6 +5,7 @@ import (
 	"bytes"
 	"context"
 	"database/sql"
+	"encoding/binary"
 	"encoding/hex"
 	"fmt"
 	"io"
@@ -952,3 +953,17 @@ func runBatch(t testing.TB, p msdsn.Config) {
 		}
 	}
 }
+
+func TestGetDriverVersion(t *testing.T) {
+	v := getDriverVersion("v1.20.3")
+	t.Logf("driverVersion %08x (%s)", v, versionToHexString(v))
+	if v != (1<<24)|(20<<16)|(3) {
+		t.Fatalf("Incorrect getDriverVersion. In: v1.20.3 Out: 0x%x", v)
+	}
+}
+
+func versionToHexString(v uint32) string {
+	b := make([]byte, 4)
+	binary.LittleEndian.PutUint32(b, v)
+	return hex.EncodeToString(b)
+}