Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions tds.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ func readPreloginOption(buffer []byte, offset int) (*preloginOption, error) {
rec_type := buffer[offset]
if rec_type == preloginTERMINATOR {
return &preloginOption{token: rec_type}, nil
} else if rec_type == preloginTHREADID {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rec_type == preloginTHREADID {

does this check need to be protected by the TDS version check too?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/60f56408-0188-4cd5-8b90-25c6f2423868 there seems to be no constraint on the TDS-Version.

As I understand the documentation, the parameter is used for the client to communicate its thread-id for debugging-purposes. If sent by the server, it should be empty in any case.

// This value SHOULD be empty when being sent from the server to the client.
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/60f56408-0188-4cd5-8b90-25c6f2423868
return &preloginOption{token: rec_type}, nil
}

// check if prelogin option exists in buffer
Expand Down
94 changes: 86 additions & 8 deletions token.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,20 +408,32 @@ func parseOrder(r *tdsBuffer) (res orderStruct) {
}

// https://msdn.microsoft.com/en-us/library/dd340421.aspx
func parseDone(r *tdsBuffer) (res doneStruct) {
func parseDone72(r *tdsBuffer) (res doneStruct) {
res.Status = r.uint16()
res.CurCmd = r.uint16()
res.RowCount = r.uint64()
return res
}
func parseDone71(r *tdsBuffer) (res doneStruct) {
res.Status = r.uint16()
res.CurCmd = r.uint16()
res.RowCount = uint64(r.uint32())
return res
}

// https://msdn.microsoft.com/en-us/library/dd340553.aspx
func parseDoneInProc(r *tdsBuffer) (res doneInProcStruct) {
func parseDoneInProc72(r *tdsBuffer) (res doneInProcStruct) {
res.Status = r.uint16()
res.CurCmd = r.uint16()
res.RowCount = r.uint64()
return res
}
func parseDoneInProc71(r *tdsBuffer) (res doneInProcStruct) {
res.Status = r.uint16()
res.CurCmd = r.uint16()
res.RowCount = uint64(r.uint32())
return res
}

type sspiMsg []byte

Expand Down Expand Up @@ -586,6 +598,24 @@ func parseColMetadata72(r *tdsBuffer) (columns []columnStruct) {
}
return columns
}
func parseColMetadata71(r *tdsBuffer) (columns []columnStruct) {
count := r.uint16()
if count == 0xffff {
// no metadata is sent
return nil
}
columns = make([]columnStruct, count)
for i := range columns {
column := &columns[i]
column.UserType = uint32(r.uint16())
column.Flags = r.uint16()

// parsing TYPE_INFO structure
column.ti = readTypeInfo(r)
column.ColName = r.BVarChar()
}
return columns
}

// http://msdn.microsoft.com/en-us/library/dd357254.aspx
func parseRow(r *tdsBuffer, columns []columnStruct, row []interface{}) {
Expand Down Expand Up @@ -621,9 +651,21 @@ func parseError72(r *tdsBuffer) (res Error) {
res.LineNo = r.int32()
return
}
func parseError71(r *tdsBuffer) (res Error) {
length := r.uint16()
_ = length // ignore length
res.Number = r.int32()
res.State = r.byte()
res.Class = r.byte()
res.Message = r.UsVarChar()
res.ServerName = r.BVarChar()
res.ProcName = r.BVarChar()
res.LineNo = int32(r.uint16())
return
}

// http://msdn.microsoft.com/en-us/library/dd304156.aspx
func parseInfo(r *tdsBuffer) (res Error) {
func parseInfo72(r *tdsBuffer) (res Error) {
length := r.uint16()
_ = length // ignore length
res.Number = r.int32()
Expand All @@ -635,6 +677,18 @@ func parseInfo(r *tdsBuffer) (res Error) {
res.LineNo = r.int32()
return
}
func parseInfo71(r *tdsBuffer) (res Error) {
length := r.uint16()
_ = length // ignore length
res.Number = r.int32()
res.State = r.byte()
res.Class = r.byte()
res.Message = r.UsVarChar()
res.ServerName = r.BVarChar()
res.ProcName = r.BVarChar()
res.LineNo = int32(r.uint16())
return
}

// https://msdn.microsoft.com/en-us/library/dd303881.aspx
func parseReturnValue(r *tdsBuffer) (nv namedValue) {
Expand Down Expand Up @@ -714,7 +768,12 @@ func processSingleResponse(ctx context.Context, sess *tdsSession, ch chan tokenS
order := parseOrder(sess.buf)
ch <- order
case tokenDoneInProc:
done := parseDoneInProc(sess.buf)
var done doneInProcStruct
if sess.loginAck.TDSVersion <= verTDS71rev1 {
done = parseDoneInProc71(sess.buf)
} else {
done = parseDoneInProc72(sess.buf)
}

ch <- done
if done.Status&doneCount != 0 {
Expand Down Expand Up @@ -742,7 +801,12 @@ func processSingleResponse(ctx context.Context, sess *tdsSession, ch chan tokenS
return
}
case tokenDone, tokenDoneProc:
done := parseDone(sess.buf)
var done doneStruct
if sess.loginAck.TDSVersion <= verTDS71rev1 {
done = parseDone71(sess.buf)
} else {
done = parseDone72(sess.buf)
}
done.errors = errs
if outs.msgq != nil {
errs = make([]Error, 0, 5)
Expand Down Expand Up @@ -781,7 +845,11 @@ func processSingleResponse(ctx context.Context, sess *tdsSession, ch chan tokenS
return
}
case tokenColMetadata:
columns = parseColMetadata72(sess.buf)
if sess.loginAck.TDSVersion <= verTDS71rev1 {
columns = parseColMetadata71(sess.buf)
} else {
columns = parseColMetadata72(sess.buf)
}
ch <- columns
colsReceived = true
if outs.msgq != nil {
Expand All @@ -799,7 +867,12 @@ func processSingleResponse(ctx context.Context, sess *tdsSession, ch chan tokenS
case tokenEnvChange:
processEnvChg(ctx, sess)
case tokenError:
err := parseError72(sess.buf)
var err Error
if sess.loginAck.TDSVersion <= verTDS71rev1 {
err = parseError71(sess.buf)
} else {
err = parseError72(sess.buf)
}
if sess.logFlags&logDebug != 0 {
sess.logger.Log(ctx, msdsn.LogDebug, fmt.Sprintf("got ERROR %d %s", err.Number, err.Message))
}
Expand All @@ -811,7 +884,12 @@ func processSingleResponse(ctx context.Context, sess *tdsSession, ch chan tokenS
_ = sqlexp.ReturnMessageEnqueue(ctx, outs.msgq, sqlexp.MsgError{Error: err})
}
case tokenInfo:
info := parseInfo(sess.buf)
var info Error
if sess.loginAck.TDSVersion <= verTDS71rev1 {
info = parseInfo71(sess.buf)
} else {
info = parseInfo72(sess.buf)
}
if sess.logFlags&logDebug != 0 {
sess.logger.Log(ctx, msdsn.LogDebug, fmt.Sprintf("got INFO %d %s", info.Number, info.Message))
}
Expand Down